投稿的网站,WordPress免费主题商城,广州微网站建设效果,网络推广seo是什么《Linux C/C服务器开发实践》之第4章 TCP服务器编程 4.1 套接字的基本概念4.2 网络程序的架构4.3 IP地址的格式转换4.1.c 4.4 套接字的类型4.5 套接字地址4.5.1 通用socket地址4.5.2 专用socket地址4.5.3 获取套接字地址4.2.c 4.6 主机字节序和网络字节序4.3.c 4.7 协议族和地址… 《Linux C/C服务器开发实践》之第4章 TCP服务器编程 4.1 套接字的基本概念4.2 网络程序的架构4.3 IP地址的格式转换4.1.c 4.4 套接字的类型4.5 套接字地址4.5.1 通用socket地址4.5.2 专用socket地址4.5.3 获取套接字地址4.2.c 4.6 主机字节序和网络字节序4.3.c 4.7 协议族和地址族4.8 TCP套接字编程的基本步骤4.9 TCP套接字编程的相关函数4.9.1 BSD socket的头文件4.9.2 socket函数4.9.3 bind函数4.9.4 listen函数4.9.5 accept函数4.9.6 connect函数4.9.7 send函数4.9.8 recv函数4.9.9 close函数 4.10 简单的TCP套接字编程4.4.server.c4.4.client.c4.5.c 4.11 深入理解TCP编程4.11.1 数据发送和接收涉及的缓冲区4.11.2 TCP数据传输的特点4.11.3 数据发送的六种情形4.11.4 数据接收时的情形4.11.5 一次请求响应的数据接收4.6.server.c4.6.client.c 4.11.6 多次请求响应的数据接收4.7.server.c4.7.client.c4.8.c4.9.server.c4.9.client.c 4.12 I/O控制命令4.10.c 4.13 套接字选项4.13.1 基本概念4.13.2 选项的级别4.13.3 获取套接字选项4.11.c4.12.c4.13.c 4.13.4 设置套接字选项4.14.c 4.1 套接字的基本概念
套接字是TCP/IP网络编程中的基本操作单元不同主机的进程之间的相互通信的端点。 socket是在应用层和传输层之间的一个抽象层它把TCP/IC层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
4.2 网络程序的架构
B/SBrowser/Server浏览器/服务器架构用户只需要浏览器就行主要逻辑在服务器完成减轻了客户端的升级和维护的工作量。
C/SClient/Server客户机/服务器架构客户端和服务端安装不同应用软件客户端软件安装或升级比较复杂维护成本大可以充分利用两端的硬件能力较为合理的分配任务。
客户机和服务器间的通信过程 1客户机向服务器提出一个请求。 2服务器收到客户机的请求进行分析处理。 3服务器将处理的结果返回给客户机。
4.3 IP地址的格式转换
#include arpa/inet.h
//字符串IP地址转换为网络字节序存储在addr中并返回该网络字节序表示的无符号整数。
//失败返回0
int inet_aton(const char *IP, struct in_addr *addr);//返回网络字节序
//uint32失败-1
in_addr_t inet_addr(const char* cp);//及时复制返回的字符串
char *inet_ntoa(struct in_addr in);#include arpa/inet.h
#include stdlib.h
#include iostreamint main()
{char IP[] 159.12.8.109;in_addr address;int number inet_aton(IP, address);//将点分十进制的IP地址转化为二进制的网络字节序if(number 0){std::cerrerror IP!;exit(1);}std::cout number std::endl;std::cout inet_ntoa(address) std::endl;//将网络字节序地址转化为点分十进制表示形式return 0;
}4.1.c
#include stdio.h
#include arpa/inet.hint main()
{in_addr_t dwIP inet_addr(172.16.2.6);struct in_addr ia;ia.s_addr dwIP;printf(ia.s_addr %#x\n, ia.s_addr);printf(real_ip %s\n, inet_ntoa(ia));return 0;
}4.4 套接字的类型
1流套接字SOCK_STREAM 用于提供面向连接的、可靠的数据传输服务无数据边界收发次数不一致可保证数据能够无差别、无重复发送并按顺序接收因为使用了传输控制协议即TCP。
2数据报套接字SOCK_DGRAM 提供无连接的服务不保证数据传输的可靠性有数据边界数据报收发次数一致数据传输过程中可能丢失或重复且无法保证接收数据有序。使用UDP传输
3原始套接字SOCK_RAW 允许对较低层次的协议IP、ICMP等直接访问常用于检验新的协议实现或者访问现有服务中配置的新设备。 能够控制网络底层传输机制所以可以应用原始套接字操纵网络层和传输层应用。接收ICMP、IGMP协议包接收TCP/IP栈不能处理的IP包发送自定义报头或自定义协议的IP包。 能够读写内核没有处理的IP数据报。
4.5 套接字地址
包含IP地址和端口信息识别主机及进程。
4.5.1 通用socket地址
表示大多数网络地址用于socket API函数中。
struct sockaddr{sa_family_t sa_family;char sa_data[14];
};sa_family_t地址族或协议族类型
PF_UNIX: UNIX本地域协议族PF_INET: IPv4协议族PF_INET6: IPv6协议族AF_UNIX: UNIX本地域地址族AF_INET: IPv4地址族AF_INET: IPv6地址族
sa_data存放具体的地址数据IP和端口
协议族地址的含义和长度PF_INET32位IPV4地址和16位端口号共6字节PF_INET6128位IPv6地址、16位端口号、32位流标识和32位范围ID共26字节PF_UNIX文件全路劲名最大长度可达108字节
4.5.2 专用socket地址
不同协议族定义的不同socket地址结构体各个信息用不同字段表示。 一般强制转换为通用地址结构使用。
/* Structure describing an Internet socket address. */
struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port; /* Port number. */struct in_addr sin_addr; /* Internet address. *//* Pad to size of struct sockaddr. */unsigned char sin_zero[sizeof (struct sockaddr)- __SOCKADDR_COMMON_SIZE- sizeof (in_port_t)- sizeof (struct in_addr)];};/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr{in_addr_t s_addr;};#if !__USE_KERNEL_IPV6_DEFS
/* Ditto, for IPv6. */
struct sockaddr_in6{__SOCKADDR_COMMON (sin6_);in_port_t sin6_port; /* Transport layer port # */uint32_t sin6_flowinfo; /* IPv6 flow information */struct in6_addr sin6_addr; /* IPv6 address */uint32_t sin6_scope_id; /* IPv6 scope-id */};
#endif /* !__USE_KERNEL_IPV6_DEFS */#if !__USE_KERNEL_IPV6_DEFS
/* IPv6 address */
struct in6_addr{union{uint8_t __u6_addr8[16];uint16_t __u6_addr16[8];uint32_t __u6_addr32[4];} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif};
#endif /* !__USE_KERNEL_IPV6_DEFS */4.5.3 获取套接字地址
#include sys/socket.h
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
//成功0失败-1
//socklen_t addrlen sizeof(struct sockaddr_in);getsockname获取本地套接字地址的情况
本地套接字已bind地址本地套接字已connet到远程内核会分配地址
#include sys/socket.h
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
//成功0失败-1
//socklen_t addrlen sizeof(struct sockaddr_in);4.2.c
#include stdio.h
#include sys/socket.h
#include arpa/inet.h
#include string.h
#include errno.hint main()
{int sfp socket(AF_INET, SOCK_STREAM, 0);if (-1 sfp){printf(socket() fail!\n);return -1;}printf(socket() ok!\n);char on 1;setsockopt(sfp, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));struct sockaddr_in serv {0};int serv_len sizeof(serv);printf(ip%s, port%d\n, inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));getsockname(sfp, (struct sockaddr *)serv, (socklen_t *)serv_len);printf(ip%s, port%d\n, inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));unsigned short portnum 10051;struct sockaddr_in s_add;memset(s_add, 0, sizeof(struct sockaddr_in));s_add.sin_family AF_INET;s_add.sin_addr.s_addr inet_addr(127.0.0.1);s_add.sin_port htons(portnum);if (-1 bind(sfp, (struct sockaddr *)(s_add), sizeof(struct sockaddr))){printf(bind() fail: %d!\n, errno);return -1;}printf(bind() ok!\n);getsockname(sfp, (struct sockaddr *)serv, (socklen_t *)serv_len);printf(ip%s, port%d\n, inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));return 0;
}4.6 主机字节序和网络字节序
小端字节序Little Endian 底地址存放低字节
大端字节序Big Endian网络字节序 底地址存放高字节
uint32_t主机字节序转网络字节序
htonl
uint16_t主机字节序转网络字节序
htonsuint32_t网络字节序转主机字节序
ntohl
uint16_t网络字节序转主机字节序
ntohs4.3.c
#include iostream
using namespace std;int main()
{int nNum 0x12345678;char *p (char *)nNum;if (*p 0x12)cout This machine is big endian. endl;elsecout This machine is small endian. endl;return 0;
}4.7 协议族和地址族 协议族 不同协议的集合宏以PF_开头PROTOCOL FAMILY 地址族 协议族所使用的地址集合不同网络协议使用不同网络地址宏以AF_开头Address Family
地址族和协议族的值一样都用来标识不同的一套协议。
4.8 TCP套接字编程的基本步骤
服务器编程步骤 一、创建套接字socket函数 二、绑定套接字到IP地址和端口bind函数 三、套接字设置为监听模式并等待连接请求listen函数 四、请求到来时接受连接请求返回对应连接的套接字accept函数 五、用该连接套接字同客户端通信send或recv函数通信结束关闭closesocket函数 六、监听套接字等待其他客户端的连接 七、推出服务器程序关闭监听套接字closesocket函数
客户端编程步骤 一、创建套接字socket函数 二、向服务器发出请求连接connect函数 三、同服务器端通信send或recv函数 四、通信结束关闭closesocket函数
4.9 TCP套接字编程的相关函数
4.9.1 BSD socket的头文件
sys/socket.h核心函数和数据结构的声明netinet/in.h地址族和协议族IP地址和端口号等bits/socket.h地址族和协议族的宏定义arpa/inet.hIP地址相关函数netdb.h协议名和主机名转化为数字的函数
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include unistd.h4.9.2 socket函数
创建套接字并分配系统资源
int socket(int domain, int type, int protocol);
//AF_INET
//SOCK_STREAM, SOCK_DGRAM, SOCK_RAW
//IPPROTO_TCP, IPPROTO_UDP, 0
//默认都是阻塞4.9.3 bind函数
本地地址信息关联到套接字上。
int bind(int sockfd, const struct* addr, socklen_t addrlen);
//成功0失败-1errno获取错误码sockaddr_in in;
in_addr_t ip inet_addr(192.168.13.25);
if(ip ! -1)in.sin_addr.s_addr ip;//#define INADDR_ANY ((in_addr_t) 0x00000000)
in.sin_addr.s_addr htonl(INADDR_ANY);//errno98端口占用、未释放或程序未正常结束4.9.4 listen函数
套接字处于监听状态。
int listen(int sockfd, int backlog);
//成功0失败-14.9.5 accept函数
从监听套接字的客户连接请求队列获取客户端请求并创建新的套接字来和客户端通信。
int accept(int sockfd, struct sockaddr *addr, socklen_t * addrlen);
//失败-14.9.6 connect函数
请求与监听套接字建立连接。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//成功0失败-1非阻塞时可设置连接超时时间通过error中EINRPOCESSOperation now in progress判断。
4.9.7 send函数
发送数据复制到套接字的发送缓冲区。
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//成功返回发送拷贝字节数对方关闭返回0错误-1TCP有发送缓冲区UDP无发送缓冲区。
非阻塞可利用error变量EAGAIN
send用于有连接的套接字。
sendto和sendmsg用于有或无连接的套接字。
4.9.8 recv函数
接收数据.
ssize_t recv(int sockfd, const void *buf, size_t len, int flags);
//成功返回接收字节数对方关闭返回0错误-1errno是EINTR、EWOULDBLOCK或EAGAIN时连接正常recvfrom也能接收数据。
4.9.9 close函数
关闭套接字
#include unistd.h
int close(int fd);
//成功0失败-14.10 简单的TCP套接字编程
4.4.server.c
#include stdio.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.hint main()
{int sockSrv socket(AF_INET, SOCK_STREAM, 0);char on 1;setsockopt(sockSrv, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));struct sockaddr_in addrSrv;memset(addrSrv, 0, sizeof(struct sockaddr_in));addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);if (-1 bind(sockSrv, (struct sockaddr *)addrSrv, sizeof(struct sockaddr))){printf(bind() fail: %d!\n, errno);return -1;}const int len sizeof(struct sockaddr_in);struct sockaddr_in serv;getsockname(sockSrv, (struct sockaddr *)serv, (socklen_t *)len);printf(server has started, ip%s, port%d\n, inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));listen(sockSrv, 5);struct sockaddr_in addrClient;while (1){printf(--------wait for client-----------\n);int sockConn accept(sockSrv, (struct sockaddr *)addrClient, (socklen_t *)len);char sendBuf[100];sprintf(sendBuf, Welcome client(%s: %d) to Server!, inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));send(sockConn, sendBuf, strlen(sendBuf) 1, 0);char recvBuf[100];recv(sockConn, recvBuf, 100, 0);printf(Receive clients msg: %s\n, recvBuf);close(sockConn);/*puts(continue to listen?(y/n));char ch[2];scanf(%s, ch, 2);if (ch[0] ! y)break;*/}close(sockSrv);return 0;
}4.4.client.c
#include stdio.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.hint main()
{int sockClient socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addrSrv;addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);int err connect(sockClient, (struct sockaddr *)addrSrv, sizeof(struct sockaddr));if (-1 err){printf(Failed to connect to the server.Please check whether the server is started\n);return 0;}char recvBuf[100] {0};recv(sockClient, recvBuf, 100, 0);printf(receive servers msg: %s\n, recvBuf);char msg[] hi,server;send(sockClient, msg, strlen(msg) 1, 0);close(sockClient);return 0;
}4.5.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h#define BUFFER_SIZE 512unsigned long GetTickCount()
{struct timeval tv;if (gettimeofday(tv, NULL) ! 0)return 0;return (tv.tv_sec * 1000) (tv.tv_usec / 1000);
}int main()
{struct sockaddr_in server_address;memset(server_address, 0, sizeof(server_address));server_address.sin_family AF_INET;server_address.sin_addr.s_addr inet_addr(192.168.0.88);server_address.sin_port htons(13334);int sock socket(PF_INET, SOCK_STREAM, 0);assert(sock 0);long t1 GetTickCount();int ret connect(sock, (struct sockaddr *)server_address, sizeof(server_address));if (ret -1){long t2 GetTickCount();printf(connect() failed: %d\n, ret);printf(time used: %ldms\n, t2 - t1);if (errno EINPROGRESS)printf(unblock mode ret code...\n);}elseprintf(ret code is: %d\n, ret);close(sock);return 0;
}4.11 深入理解TCP编程
4.11.1 数据发送和接收涉及的缓冲区
应用缓冲区和TCP套接字缓冲区内核缓冲区。
4.11.2 TCP数据传输的特点
一、字节流无消息边界。 二、send后并不立即发送数据内核控制。 三、数据发送速度网络状态决定。 四、控制数据真实发送网络状态决定。 五、recv时并不知道真实已接收多少数据。
4.11.3 数据发送的六种情形
假设调用两次send发送数据A和数据B。send(A)send(B)真实发送情况 一、网络情况良好未受发送窗口、拥塞窗口和TCP最大传输单元影响A、B变成两个数据段发送。 二、网络不好发送A被延迟A、B数据合并且长度未超过窗口大小和最大传输单元。AB合并发送一次。 三、网络不好发送A被延迟A、B数据合并且长度超过窗口大小或最大传输单元。AB合并发送AB1、B2。 四、网络不好发送A被延迟A、B数据合并且长度超过窗口大小或最大传输单元。AB合并发送A1、A2B。 五、接收窗口小AB分成多份发送。 六、发送错误失败。
4.11.4 数据接收时的情形
接收到本次达到接收端的全部数据接收到达到接收端的部分数据没有收到数据
send与实际发送次数无关send与recv次数无关。
4.11.5 一次请求响应的数据接收
接收到全部数据后断开连接通过recv返回0判断发送方数据发送完毕。
4.6.server.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h#define BUF_LEN 300typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;int main()
{int sockSrv socket(AF_INET, SOCK_STREAM, 0);assert(sockSrv 0);char on 1;setsockopt(sockSrv, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));SOCKADDR_IN addrSrv;addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);bind(sockSrv, (SOCKADDR *)addrSrv, sizeof(SOCKADDR));listen(sockSrv, 5);const int len sizeof(SOCKADDR);SOCKADDR_IN addrClient;while (1){printf(--------wait for client-----------\n);int sockConn accept(sockSrv, (SOCKADDR *)addrClient, (socklen_t *)len);char sendBuf[100] ;for (int i 0; i 10; i){memset(sendBuf, 0, sizeof(sendBuf));sprintf(sendBuf, N0.%d Welcome to the server. What is 1 1 ? (client IP: %s, client Port: %d)\n, i 1, inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));send(sockConn, sendBuf, strlen(sendBuf), 0);}int iRes shutdown(sockConn, SHUT_WR);if (iRes -1){printf(shutdown failed with error: %d\n, errno);close(sockConn);return 1;}char recvBuf[BUF_LEN];do{iRes recv(sockConn, recvBuf, BUF_LEN, 0);if (iRes 0){printf(Recv %d bytes.\n, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){printf(The client has closed the connection.\n);}else{printf(recv failed with error: %d\n, errno);close(sockConn);return 1;}} while (iRes 0);close(sockConn);/*puts(Continue monitoring?(y/n));char ch[2];scanf(%s, ch, 2);if (ch[0] ! y)break;*/}close(sockSrv);return 0;
}4.6.client.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h#define BUF_LEN 300typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;int main()
{int sockClient socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);int err connect(sockClient, (SOCKADDR *)addrSrv, sizeof(SOCKADDR));if (-1 err){printf(Failed to connect to the server. Please check whether the server is started\n);return 0;}char recvBuf[BUF_LEN];int iRes;do{iRes recv(sockClient, recvBuf, BUF_LEN, 0);if (iRes 0){printf(\nRecv %d bytes:, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){puts(The server has closed the send connection.\n);}else{printf(recv failed:%d\n, errno);close(sockClient);return 1;}} while (iRes 0);char sendBuf[100];for (int i 0; i 10; i){memset(sendBuf, 0, sizeof(sendBuf));sprintf(sendBuf, N0.%d Im the client, 112\n, i 1);send(sockClient, sendBuf, strlen(sendBuf) 1, 0);}puts(Sending data to the server is completed.);close(sockClient);return 0;
}4.11.6 多次请求响应的数据接收
定长数据的接收
4.7.server.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h#define BUF_LEN 300typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;int main()
{int sockSrv socket(AF_INET, SOCK_STREAM, 0);assert(sockSrv 0);char on 1;setsockopt(sockSrv, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));SOCKADDR_IN addrSrv;addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);bind(sockSrv, (SOCKADDR *)addrSrv, sizeof(SOCKADDR));listen(sockSrv, 5);const int len sizeof(SOCKADDR);SOCKADDR_IN addrClient;char sendBuf[111];char recvBuf[BUF_LEN];while (1){printf(--------wait for client-----------\n);int sockConn accept(sockSrv, (SOCKADDR *)addrClient, (socklen_t *)len);printf(--------client comes-----------\n);memset(sendBuf, a, 111);for (int cn 0; cn 50; cn){if (cn 49)sendBuf[110] b;send(sockConn, sendBuf, 111, 0);}int iRes;do{iRes recv(sockConn, recvBuf, BUF_LEN, 0);if (iRes 0){printf(\nRecv %d bytes:, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){printf(The client closes the connection.\n);}else{printf(recv failed with error: %d\n, errno);close(sockConn);return 1;}} while (iRes 0);close(sockConn);/*puts(Continue monitoring?(y/n));char ch[2];scanf(%s, ch, 2);if (ch[0] ! y)break;*/}close(sockSrv);return 0;
}4.7.client.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h#define BUF_LEN 250typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;int main()
{int sockClient socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);int err connect(sockClient, (SOCKADDR *)addrSrv, sizeof(SOCKADDR));if (-1 err){printf(Failed to connect to the server:%d\n, errno);return 0;}char recvBuf[BUF_LEN];int iRes;int cn 1;for (int leftlen 50 * 111; leftlen 0; leftlen - iRes){iRes recv(sockClient, recvBuf, BUF_LEN, 0);if (iRes 0){printf(\nNo.%d: Recv %d bytes: , cn, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){puts(\nThe server has closed the send connection.\n);}else{printf(recv failed: %d\n, errno);close(sockClient);return -1;}}/*int leftlen 50 * 111;while (leftlen BUF_LEN){iRes recv(sockClient, recvBuf, BUF_LEN, 0);if (iRes 0){printf(\nNo.%d: Recv %d bytes: , cn, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){puts(\nThe server has closed the send connection.\n);}else{printf(recv failed: %d\n, errno);close(sockClient);return -1;}leftlen - iRes;}if (leftlen 0){iRes recv(sockClient, recvBuf, leftlen, 0);if (iRes 0){printf(\nNo.%d: Recv %d bytes: , cn, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){puts(\nThe server has closed the send connection.\n);}else{printf(recv failed: %d\n, errno);close(sockClient);return -1;}leftlen - iRes;}
*/char sendBuf[100];memset(sendBuf, 0, sizeof(sendBuf));sprintf(sendBuf, Hi, Server, Ive finished receiving the data.);send(sockClient, sendBuf, strlen(sendBuf) 1, 0);puts(Sending data to the server is completed);close(sockClient);return 0;
}变长数据的接收 数据报末尾加结束标识符 变长消息体前加固定长度的报头报头内加入消息体长度字段
struct MyData
{int nLen;char data[0];
};
struct MyData* p (struct MyData*)malloc(sizeof(struct MyData)strlen(str));4.8.c
#include cstdio
#include iostream
#include string.h
using namespace std;struct MyData
{int nLen;char data[0];
};int main()
{cout Size of MyData: sizeof(MyData) endl;char str[10] 123456;int nLen sizeof(str);MyData *myData (MyData *)malloc(sizeof(MyData) nLen);myData-nLen nLen;memcpy(myData-data, str, nLen);cout myDatas Data is: myData-data endl;cout Size of MyData: sizeof(MyData) endl;free(myData);return 0;
}4.9.server.c
#include cstdio
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include malloc.h#define BUF_LEN 300typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;struct MyData
{int nLen;char data[0];
};int main()
{int sockSrv socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);bind(sockSrv, (SOCKADDR *)addrSrv, sizeof(SOCKADDR));listen(sockSrv, 5);SOCKADDR_IN addrClient;int len sizeof(SOCKADDR);int cn 5550;struct MyData *mydata;int iRes;char recvBuf[BUF_LEN];while (1){printf(--------wait for client-----------\n);int sockConn accept(sockSrv, (SOCKADDR *)addrClient, (socklen_t *)len);printf(--------client comes-----------\n);mydata (MyData *)malloc(sizeof(MyData) cn);mydata-nLen htonl(cn);memset(mydata-data, a, cn);mydata-data[cn - 1] b;send(sockConn, (char *)mydata, sizeof(MyData) cn, 0);free(mydata);do{iRes recv(sockConn, recvBuf, BUF_LEN, 0);if (iRes 0){printf(\nRecv %d bytes: , iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){printf(\nThe client has closed the connection.\n);}else{printf(recv failed with error: %d\n, errno);close(sockConn);return 1;}} while (iRes 0);close(sockConn);/*puts(Continue monitoring?(y/n));char ch[2];scanf(%s, ch, 2);if (ch[0] ! y)break;*/}close(sockSrv);return 0;
}4.9.client.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include malloc.h#define BUF_LEN 250typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;int main()
{int sockClient socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_addr.s_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(8000);int err connect(sockClient, (SOCKADDR *)addrSrv, sizeof(SOCKADDR));if (-1 err){printf(Failed to connect to the server:%d\n, errno);return 0;}int leftlen;int iRes recv(sockClient, (char *)leftlen, sizeof(int), 0);leftlen ntohl(leftlen);printf(Need to receive %d bytes data.\n, leftlen);char recvBuf[BUF_LEN];int cn 1;for (; leftlen 0; leftlen - iRes){if (leftlen BUF_LEN)iRes recv(sockClient, recvBuf, leftlen, 0);elseiRes recv(sockClient, recvBuf, BUF_LEN, 0);if (iRes 0){printf(\nNo.%d: Recv %d bytes: , cn, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){puts(\nThe server has closed the send connection.\n);}else{printf(recv failed: %d\n, errno);close(sockClient);return -1;}}/*while (leftlen BUF_LEN){iRes recv(sockClient, recvBuf, BUF_LEN, 0);if (iRes 0){printf(\nNo.%d: Recv %d bytes: , cn, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){puts(\nThe server has closed the send connection.\n);}else{printf(recv failed: %d\n, errno);close(sockClient);return -1;}leftlen - iRes;}if (leftlen 0){iRes recv(sockClient, recvBuf, leftlen, 0);if (iRes 0){printf(\nNo.%d: Recv %d bytes: , cn, iRes);for (int i 0; i iRes; i)printf(%c, recvBuf[i]);printf(\n);}else if (iRes 0){puts(\nThe server has closed the send connection.\n);}else{printf(recv failed: %d\n, errno);close(sockClient);return -1;}leftlen - iRes;}*/char sendBuf[100];memset(sendBuf, 0, sizeof(sendBuf));sprintf(sendBuf, Im the client. Ive finished receiving the data.);send(sockClient, sendBuf, strlen(sendBuf) 1, 0);puts(Sending data to the server is completed);close(sockClient);return 0;
}4.12 I/O控制命令
设置套接字的工作模式阻塞或非阻塞获取套接字I/O操作的参数信息。
#include sys/ioctl.h
int ioctl(int fd, int request, ...);
//成功0失败-1errno错误码I/O控制命令request
FIONBIO 阻塞FIONREAD 流套接字recv一次可读入数据量数据报套接字第一个数据报大小缓冲区大小。FIOASYNC 异步
int iMode 0;
ioctl(m_socket, FIONBIO, iMode); //阻塞模式int num 0;
ioctl(0, FIONREAD, iMode); //标准输入缓冲区字节数4.10.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.hunsigned long GetTickCount()
{struct timeval tv;if (gettimeofday(tv, NULL) ! 0)return 0;return (tv.tv_sec * 1000) (tv.tv_usec / 1000);
}int main()
{char ip[] 192.168.0.88;in_addr_t dwIP inet_addr(ip);int port 13334;struct sockaddr_in server_address;memset(server_address, 0, sizeof(server_address));server_address.sin_family AF_INET;server_address.sin_addr.s_addr dwIP;server_address.sin_port htons(port);int sock socket(PF_INET, SOCK_STREAM, 0);assert(sock 0);long t1 GetTickCount();int ret connect(sock, (struct sockaddr *)server_address, sizeof(server_address));printf(connect ret code is: %d\n, ret);if (ret -1){long t2 GetTickCount();printf(time used: %ldms\n, t2 - t1);printf(connect() failed...\n);if (errno EINPROGRESS)printf(unblock mode ret code...\n);}elseprintf(ret code is: %d\n, ret);int argp 1;int res ioctl(sock, FIONBIO, argp);if (-1 res){printf(Error at ioctlsocket(): %d\n, errno);return -1;}puts(\nAfter setting non blocking mode:);/*memset(server_address, 0, sizeof(server_address));server_address.sin_family AF_INET;server_address.sin_addr.s_addr dwIP;server_address.sin_port htons(port);*/t1 GetTickCount();ret connect(sock, (struct sockaddr *)server_address, sizeof(server_address));printf(connect ret code is: %d\n, ret);if (ret -1){long t2 GetTickCount();printf(time used: %ldms\n, t2 - t1);if (errno EINPROGRESS)printf(unblock mode errno: %d\n, errno);}elseprintf(ret code is: %d\n, ret);close(sock);return 0;
}4.13 套接字选项
4.13.1 基本概念
设置或获取套接字属性
4.13.2 选项的级别
适用范围或适用对象有些选项针对特定协议有些选项适用所有类型套接字。 SO_TYPE SO_SNDBUF SO_REUSEADDR SO_RCVBUF SO_ERROR …
4.13.3 获取套接字选项
#include sys/types.h
#include sys/socket.h
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
//成功0失败-1errno错误码
//EBADFsockfd不是有效文件描述符
//EFAULToptlen太小或optval缓冲区非法
//EINVALlevel未知或非法
//ENOPROTOOPT选项未知或不被指定协议族支持
//ENOTSOCKsockfd不是套接字描述符4.11.c
#include stdio.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.hint main()
{int s socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (s -1){printf(Error at socket()\n);return -1;}int su socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (s -1){printf(Error at socket()\n);return -1;}int optVal;int optLen sizeof(optVal);if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed: %d, errno);elseprintf(Size of stream socket receive buffer: %d bytes\n, optVal);if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed: %d, errno);elseprintf(Size of streaming socket send buffer: %d bytes\n, optVal);if (getsockopt(su, SOL_SOCKET, SO_RCVBUF, (char *)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed: %d, errno);elseprintf(Size of datagram socket receive buffer: %d bytes\n, optVal);if (getsockopt(su, SOL_SOCKET, SO_SNDBUF, (char *)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed: %d, errno);elseprintf(Size of datagram socket send buffer: %d bytes\n, optVal);return 0;
}4.12.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.hint main()
{int s socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (s -1){printf(Error at socket()\n);return -1;}int optVal;int optLen sizeof(optVal);if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed: %d, errno);else{if (SOCK_STREAM optVal)printf(The current socket is a stream socket.\n);else if (SOCK_DGRAM optVal)printf(The current socket is a datagram socket.\n);}int su socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (su -1){printf(Error at socket()\n);return -1;}if (getsockopt(su, SOL_SOCKET, SO_TYPE, (char *)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed: %d, errno);else{if (SOCK_STREAM optVal)printf(The current socket is a stream socket.\n);else if (SOCK_DGRAM optVal)printf(The current socket is a datagram socket.\n);}return 0;
}4.13.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.htypedef struct sockaddr SOCKADDR;int main()
{int s socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (s -1){printf(Error at socket()\n);return -1;}char on 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));char ip[] 127.0.0.1;struct sockaddr_in service;service.sin_family AF_INET;service.sin_addr.s_addr inet_addr(ip);service.sin_port htons(8000);if (bind(s, (SOCKADDR *)service, sizeof(service)) -1){printf(bind failed: %d\n, errno);return -1;}int optVal;int optLen sizeof(optVal);if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char *)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed: %d, errno);elseprintf(Before listening, The value of SO_ACCEPTCONN: %d, The socket is not listening\n, optVal);if (listen(s, 100) -1){printf(listen failed: %d\n, errno);return -1;}if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char *)optVal, (socklen_t *)optLen) -1){printf(getsockopt failed: %d, errno);return -1;}elseprintf(After listening, The value of SO_ACCEPTCONN: %d, The socket is listening\n, optVal);return 0;
}4.13.4 设置套接字选项
#include sys/types.h
#include sys/socket.h
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t *optlen);
//成功0失败-1errno错误码4.14.c
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.htypedef struct sockaddr SOCKADDR;int main()
{int s socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (s -1){printf(Error at socket()\n);return -1;}char on 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));char ip[] 127.0.0.1;struct sockaddr_in service;service.sin_family AF_INET;service.sin_addr.s_addr inet_addr(ip);service.sin_port htons(9900);if (bind(s, (SOCKADDR *)service, sizeof(service)) -1){printf(bind failed\n);return -1;}int optVal 1;int optLen sizeof(int);if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)optVal, (socklen_t *)optLen) -1){printf(getsockopt failed: %d, errno);return -1;}elseprintf(After bind, the value of SO_KEEPALIVE: %d\n, optVal);optVal 1;if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)optVal, optLen) ! -1)printf(Successful activation of keep alive mechanism.\n);if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)optVal, (socklen_t *)optLen) -1){printf(getsockopt failed: %d, errno);return -1;}elseprintf(After setting, the value of SO_KEEPALIVE: %d\n, optVal);return 0;
}