博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
socket网络编程快速上手(二)——细节问题(2)
阅读量:5925 次
发布时间:2019-06-19

本文共 8309 字,大约阅读时间需要 27 分钟。

 

2.TCP数据包接收问题

  对初学者来说,很多都会认为:客户端与服务器最终的打印数据接收或者发送条数都该是一致的,1000条发送打印,1000条接收打印,长度都为1000。但是,事实上并不是这样,发送打印基本不会有什么问题(只是一般情况,如果发生调度或者其他情况,有可能导致差别,因此也要注意封装),接收打印却不是固定的,下面是测试代码:

测试客户端程序:

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 10 #define PORT 123411 #define MAXDATASIZE 100012 13 int main(int argc, char *argv[])14 {15 int sockfd, num;16 char buf[MAXDATASIZE + 1] = { 0};17 struct sockaddr_in server;18 int iCount = 0;19 20 if (argc != 2) 21 {22 printf("Usage:%s
\n", argv[0]);23 exit(1);24 }25 26 if ((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)27 {28 printf("socket()error\n");29 exit(1);30 }31 bzero(&server, sizeof(server));32 server.sin_family = AF_INET;33 server.sin_port = htons(PORT);34 server.sin_addr.s_addr = inet_addr(argv[1]);35 if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)36 {37 printf("connect()error\n");38 exit(1);39 }40 41 while (1)42 {43 memset(buf, 0, sizeof(buf));44 if ((num = recv(sockfd, buf, MAXDATASIZE,0)) == -1)45 {46 printf("recv() error\n");47 exit(1);48 }49 buf[num - 1]='\0';50 printf("%dth Recv Length: %d\n", iCount++, num);51 }52 53 close(sockfd);54 55 return 0;56 }
TCP客户端

 

测试服务器程序:

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 11 #define PORT 123412 #define BACKLOG 513 #define MAXDATASIZE 100014 15 int main()16 {17 int listenfd, connectfd;18 struct sockaddr_in server;19 struct sockaddr_in client;20 socklen_t addrlen;21 char szbuf[MAXDATASIZE] = { 0};22 int iCount = 0;23 int iLength = 0;24 25 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)26 {27 perror("Creating socket failed.");28 exit(1);29 }30 31 int opt = SO_REUSEADDR;32 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));33 34 bzero(&server, sizeof(server));35 server.sin_family = AF_INET;36 server.sin_port = htons(PORT);37 server.sin_addr.s_addr = htonl(INADDR_ANY);38 if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1) 39 {40 perror("Bind()error.");41 exit(1);42 } 43 if (listen(listenfd, BACKLOG) == -1)44 {45 perror("listen()error\n");46 exit(1);47 }48 49 addrlen = sizeof(client);50 if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &addrlen)) == -1) 51 {52 perror("accept()error\n");53 exit(1);54 }55 printf("You got a connection from cient's ip is %s, prot is %d\n", inet_ntoa(client.sin_addr), htons(client.sin_port));56 57 memset(szbuf, 'a', sizeof(szbuf));58 while (iCount < 1000)59 {60 iLength = send(connectfd, szbuf, sizeof(szbuf), 0);61 printf("%dth Server Send Length %d\n", iCount++, iLength);62 }63 64 printf("send over!\n");65 sleep(10);66 67 close(connectfd);68 close(listenfd);69 70 return 0;71 }
TCP服务器程序

客户端接收打印片段如下:

1 936th Recv Length: 1000  2 937th Recv Length: 1000  3 938th Recv Length: 1000  4 939th Recv Length: 1000  5 940th Recv Length: 1000  6 941th Recv Length: 1000  7 942th Recv Length: 384  8 943th Recv Length: 616  9 944th Recv Length: 1000 10 945th Recv Length: 1000 11 946th Recv Length: 1000 12 947th Recv Length: 1000 13 948th Recv Length: 1000 14 949th Recv Length: 1000 15 950th Recv Length: 1000 16 951th Recv Length: 1000 17 952th Recv Length: 1000 18 953th Recv Length: 1000 19 954th Recv Length: 1000 20 955th Recv Length: 1000 21 956th Recv Length: 1000 22 957th Recv Length: 1000 23 958th Recv Length: 1000 24 959th Recv Length: 1000 25 960th Recv Length: 1000 26 961th Recv Length: 1000 27 962th Recv Length: 384 28 963th Recv Length: 616 29 964th Recv Length: 1000 30 965th Recv Length: 1000 31 966th Recv Length: 1000 32 967th Recv Length: 1000 33 968th Recv Length: 1000 34 969th Recv Length: 1000 35 970th Recv Length: 1000 36 971th Recv Length: 1000 37 972th Recv Length: 1000 38 973th Recv Length: 1000 39 974th Recv Length: 1000 40 975th Recv Length: 1000 41 976th Recv Length: 1000 42 977th Recv Length: 1000 43 978th Recv Length: 1000 44 979th Recv Length: 1000 45 980th Recv Length: 1000 46 981th Recv Length: 1000 47 982th Recv Length: 384 48 983th Recv Length: 616 49 984th Recv Length: 1000 50 985th Recv Length: 1000 51 986th Recv Length: 1000 52 987th Recv Length: 1000 53 988th Recv Length: 1000 54 989th Recv Length: 1000 55 990th Recv Length: 1000 56 991th Recv Length: 1000 57 992th Recv Length: 1000 58 993th Recv Length: 1000 59 994th Recv Length: 1000 60 995th Recv Length: 1000 61 996th Recv Length: 1000 62 997th Recv Length: 1000 63 998th Recv Length: 1000 64 999th Recv Length: 1000 65 1000th Recv Length: 1000 66 1001th Recv Length: 1000 67 1002th Recv Length: 384 68 1003th Recv Length: 616 69 1004th Recv Length: 1000 70 1005th Recv Length: 1000 71 1006th Recv Length: 1000 72 1007th Recv Length: 1000 73 1008th Recv Length: 1000 74 1009th Recv Length: 1000 75 1010th Recv Length: 1000 76 1011th Recv Length: 1000 77 1012th Recv Length: 1000 78 1013th Recv Length: 1000 79 1014th Recv Length: 1000 80 1015th Recv Length: 1000 81 1016th Recv Length: 1000 82 1017th Recv Length: 1000 83 1018th Recv Length: 1000 84 1019th Recv Length: 1000 85 1020th Recv Length: 1000 86 1021th Recv Length: 1000 87 1022th Recv Length: 384 88 1023th Recv Length: 616 89 1024th Recv Length: 1000 90 1025th Recv Length: 1000 91 1026th Recv Length: 1000 92 1027th Recv Length: 1000 93 1028th Recv Length: 1000 94 1029th Recv Length: 1000 95 1030th Recv Length: 1000 96 1031th Recv Length: 1000 97 1032th Recv Length: 1000 98 1033th Recv Length: 1000 99 1034th Recv Length: 1000100 1035th Recv Length: 1000101 1036th Recv Length: 1000102 1037th Recv Length: 1000103 1038th Recv Length: 1000104 1039th Recv Length: 1000105 1040th Recv Length: 1000106 1041th Recv Length: 1000107 1042th Recv Length: 384108 1043th Recv Length: 616109 1044th Recv Length: 1000110 1045th Recv Length: 1000111 1046th Recv Length: 1000112 1047th Recv Length: 1000113 1048th Recv Length: 1000114 1049th Recv Length: 1000115 1050th Recv Length: 1000
客户端接收打印片段

服务器发送打印片段整理时发现丢失了,大家可以自己试试,没有问题。

 不难发现,服务器发送正常,客户端在接收时却和我们想的很不一样,但发送和接收的总数据量是一致的,就是说数据没有丢失。如果编程者认为TCP情况下发送和接收的数据长度都一致的,那就极有可能在代码中体现出这一思想,最终出现问题。

  其实,这就是所谓的“粘包”现象,Stevens很明确地已经指出了这一点,他说,“UDP是长度固定的、无连接的不可靠报文传输;TCP是有序、可靠、双向的面向连接字节流”。他没说TCP是长度固定的,有没有?当然我更倾向于这样的理解,UDP是面向报文的,报文在传输时是不能被分割的(只是从应用层来看);TCP是面向字节流的,接收多少数据完全取决于发送和接收的速度了,有多少数据recv就返回多少,数据长度并不和send保持一致,也没这个必要。

  那么这个问题怎么解决呢?其实,我们只要将recv封装一层就可以了,那就是我们熟悉的readn函数(该函数不是系统调用),代码如下:

1 int readn(int connfd, void *vptr, int n) 2 { 3     int    nleft; 4     int    nread; 5     char *ptr; 6     struct timeval     select_timeout; 7     fd_set rset; 8  9     ptr = vptr;10     nleft = n;11 12     while (nleft > 0)13     {14         FD_ZERO(&rset);15         FD_SET(connfd, &rset);16         select_timeout.tv_sec = 5;17         select_timeout.tv_usec = 0;18         if (select(connfd+1, &rset, NULL, NULL, &select_timeout) <= 0)19         {20             return -1;21         }22         if ((nread = recv(connfd, ptr, nleft, 0)) < 0)23         {24             if(errno == EINTR)25             {26                 nread = 0;27             }28             else29             {30                 return -1;31             }32         }33         else if (nread == 0)34         {35             break;36         }37         nleft -= nread;38         ptr   += nread;39     }40     return(n - nleft);41 }
readn

相应的也有writen函数

1 int writen(int connfd, void *vptr, size_t n) 2 { 3     int nleft, nwritten; 4      char    *ptr; 5  6     ptr = vptr; 7     nleft = n; 8  9     while(nleft>0)10     {11         if((nwritten = send(connfd, ptr, nleft, 0)) == ERROR)12         {13             if(errnoGet() == EINTR)14             {15                 //PRT_ERR(("EINTR\n"));16                 nwritten = 0;17             }18             else 19             {20                 //PRT_ERR(("Send() error, 0x%x\n", errnoGet()));21                 return ERROR;22             }23         }24         nleft -= nwritten;25         ptr   += nwritten;26     }27 28     return(n);29 }
writen

 

函数中为什么对EINTR进行处理后面再说,也是必不可少的。

  在处理TCP发送和接收部分时,可以说必须要使用上述封装,否则等到造成数据不完整或者不一致后再去找问题,可能就麻烦了。这个是必不可少滴。

转载于:https://www.cnblogs.com/wxyy/p/3308857.html

你可能感兴趣的文章
mysql 主从
查看>>
【Apache+Tomcat+Session+Memcache 高性能群集搭建】
查看>>
怎样在 Linux 中限制网络带宽使用
查看>>
Oracle10g常见HINT的用法
查看>>
EMC VNXe3200 lun的在线扩容
查看>>
【两地三中心】两地三中心--灾备解决方案
查看>>
制作liveusb实现centos6.2全自动无人职守安装
查看>>
SQL GROUP BY 语句
查看>>
我的友情链接
查看>>
网络营销与电子商务
查看>>
powershell查看计算机最后登录时间
查看>>
IPSEC ×××实验二:ASA IPSEC ×××
查看>>
centos EMQTTD 集群安装配置与测试验证
查看>>
HC3i论坛医疗信息化资料30个
查看>>
lvs+keepalive 比较详细的安装配置文档
查看>>
什么是Gratuitous ARP
查看>>
Dynamic ARP Inspection(DAI)动态ARP检测
查看>>
业务表构建中一些特殊字符作为列名的构建示例
查看>>
oracle11gRAC环境使用RMAN备份方案
查看>>
JavaScript基础之程序流程的三大结构
查看>>