TCP协议下:
当发送数据过长过短, 或缓冲区大小问题, 导致出现了所谓的 TCP“粘包”问题, 这是我们的俗称, TCP是流模式,并不是包;
现象解释:
TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
好了, 根据上述的理论 我们自己人为制造一起 ”粘包“
server
1 #include
2 #include
3 #include [span style="color: rgba(0, 0, 255, 1)">string.h>
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #define ERR_EXIT(m) \
11 do { \
12 perror(m);\
13 exit(EXIT_FAILURE);\
14 }while(0)
15
16 void do_service(int sockfd);
17
18 int main(int argc, const char argv【】)
19 {
20 int listenfd = socket(PF_INET, SOCK_STREAM, 0);
21 if(listenfd == -1)
22 ERR_EXIT("socket");
23
24 //地址复用
25 int on = 1;
26 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
27 ERR_EXIT("setsockopt");
28
29 struct sockaddr_in addr;
30 //代码效果参考:http://hnjlyzjd.com/xl/wz_24787.html
memset(&addr, 0, sizeof addr);31 addr.sin_family = AF_INET;
32 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
33 addr.sin_port = htons(8976);
34 if(bind(listenfd, (struct sockaddr)&addr, sizeof addr) == -1)
35 ERR_EXIT("bind");
36
37 if(listen(listenfd, SOMAXCONN) == -1)
38 ERR_EXIT("listen");
39
40 int peerfd = accept(listenfd, NULL, NULL);
41 do_service(peerfd);
42
43 close(peerfd);
4//代码效果参考:http://hnjlyzjd.com/xl/wz_24785.html
4 close(listenfd);45
46 return 0;
47 }
48
49
50
51 void do_service(int sockfd)
52 {
53 int cnt = 0;
54 char recvbuf【1024000】 = {0};
55 while(1)
56 {
57 int nread = read(sockfd, recvbuf, sizeof recvbuf);
58 if(nread == -1)
59 {
60 if(errno == EINTR)
61 continue;
62 ERR_EXIT("read");
63 }
64 else if(nread == 0)
65 {
66 printf("close ...\n");
67 exit(EXIT_SUCCESS);
68 }
69
70 printf("count = %d, receive size = %d\n", ++cnt, nread);
71 //write(sockfd, recvbuf, strlen(recvbuf));
72 memset(recvbuf, 0, sizeof recvbuf);
73 }
74 }
注意, server端的接收缓冲区应该足够大,否则无法接收 “黏在一块的数据包”
client端
1 #include
2 #include
3 #include [span style="color: rgba(0, 0, 255, 1)">string.h>
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #define ERR_EXIT(m) \
11 do { \
12 perror(m);\
13 exit(EXIT_FAILURE);\
14 }while(0)
15
16 void do_service(int sockfd);
17 void nano_sleep(double val);
18
19 int main(int argc, const char argv【】)
20 {
21 int peerfd = socket(PF_INET, SOCK_STREAM, 0);
22 if(peerfd == -1)
23 ERR_EXIT("socket");
24
25 struct sockaddr_in addr;
26 memset(&addr, 0, sizeof addr);
27 addr.sin_family = AF_INET;
28 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //localhost
29 addr.sin_port = htons(8976);
30 socklen_t len = sizeof addr;
31 if(connect(peerfd, (struct sockaddr)&addr, len) == -1)
32 ERR_EXIT("Connect");
33
34 do_service(peerfd);
35
36
37 return 0;
38 }
39
40
41
42 void do_service(int sockfd)
43 {
44 //const int kSize = 1024;
45 #define SIZE 1024
46 char sendbuf【SIZE + 1】 = {0};
47 int i;
48 for(i = 0; i < SIZE; ++i)
49 sendbuf【i】 = 'a';
50
51 int cnt = 0; //次数
52 while(1)
53 {
54 int i;
55 for(i = 0; i < 10; ++i)
56 {
57 write(sockfd, sendbuf, SIZE);
58 printf("count = %d, write %d bytes\n", ++cnt, SIZE);
59 }
60 nano_sleep(4);
61
62 memset(sendbuf, 0, sizeof sendbuf);
63 }
64 }
65
66 void nano_sleep(double val)
67 {
68 struct timespec tv;
69 tv.tv_sec = val; //取整
70 tv.tv_nsec = (val - tv.tv_sec) 1000 1000 * 1000;
71
72 int ret;
73 do
74 {
75 ret = nanosleep(&tv, &tv);
76 }while(ret == -1 && errno == EINTR);
77 }
客户端应该 短时间发送 大量的数据, 使server端 处理接收时 造成粘包;
可以看到我们连续发送了 10次 长度为1024 的全是a的 字符串; 看下server端打印如何
count = 1, receive size = 1024
count = 2, receive size = 1024
count = 3, receive size = 1024
count = 4, receive size = 1024
count = 5, receive size = 1024
count = 6, receive size = 5120
count = 7, receive size = 10240
count = 8, receive size = 10240
count = 9, receive size = 10240
可以看到, 当第6次读取时便出现了粘包; 数据出现了相连的问题;
而我们的客户端 是均匀的每次发送1024字节的数据
count = 1, write 1024 bytes
count = 2, write 1024 bytes
count = 3, write 1024 bytes
count = 4, write 1024 bytes
count = 5, write 1024 bytes
count = 6, write 1024 bytes
count = 7, write 1024 bytes
count = 8, write 1024 bytes
count = 9, write 1024 bytes
count = 10, write 1024 bytes
count = 11, write 1024 bytes
count = 12, write 1024 bytes
count = 13, write 1024 bytes
count = 14, write 1024 bytes
count = 15, write 1024 bytes
count = 16, write 1024 bytes
count = 17, write 1024 bytes
count = 18, write 1024 bytes
count = 19, write 1024 bytes
count = 20, write 1024 bytes
count = 21, write 1024 bytes
count = 22, write 1024 bytes
count = 23, write 1024 bytes
count = 24, write 1024 bytes
count = 25, write 1024 bytes
count = 26, write 1024 bytes
count = 27, write 1024 bytes
count = 28, write 1024 bytes
count = 29, write 1024 bytes
count = 30, write 1024 bytes
count = 31, write 1024