技术笔记:Linux学习:TCP粘包问题

简介: 技术笔记:Linux学习:TCP粘包问题

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

相关文章
|
2天前
|
Unix Shell Linux
技术笔记:linux中SIGHUP与nohup的关系
技术笔记:linux中SIGHUP与nohup的关系
|
2天前
|
Unix 关系型数据库 Linux
技术笔记:linux学习心得
技术笔记:linux学习心得
|
3天前
|
机器学习/深度学习 Unix Java
技术笔记:Linux之Shell脚本编程(一)
技术笔记:Linux之Shell脚本编程(一)
|
3天前
|
监控 网络协议 Linux
技术好文共享::Linux系统日志管理日志转储
技术好文共享::Linux系统日志管理日志转储
|
2天前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
|
1天前
|
Linux 数据处理
探索Linux下的readelf命令:深入了解ELF文件
`readelf`是Linux下分析ELF文件的命令行工具,用于查看文件头、节区、符号表等信息。支持可执行文件、共享库等多种类型。常用选项有`-h`(文件头)、`-l`(程序头)、`-S`(节区)、`-s`(符号表)、`-r`(重定位)和`-d`(动态节区)。结合其他工具如`objdump`,能深入理解二进制文件,助力开发和调试。
|
2天前
|
IDE Linux 数据处理
探索Linux中的`pydoc`命令:Python文档生成器的力量
`pydoc`是Linux上Python的文档生成和查看工具,尤其对数据科学家有价值。它从docstring生成模块、函数和类的文档,提供快速API参考。主要特点包括易用性、支持标准库和第三方库、跨平台。命令行示例:`pydoc pandas` 查看库文档,`pydoc numpy.array` 查看类详情,`pydoc -k 关键字` 进行搜索。使用时注意正确安装Python,编写清晰的docstring,并结合IDE以提升效率。
|
2天前
|
存储 算法 安全
深入理解Linux命令pwscore:密码质量的守护者
**pwscore命令详解:Linux密码强度评估工具** pwscore是Linux下的密码强度检查工具,分析密码长度、字符类型及避免常见模式来评分。它提供简单语法、可定制选项和高效评估。例如,`pwscore -l 12 -m alnum`评估至少含12个字符和字母数字的密码。应用时,定期评估用户密码,制定强密码策略,避免常见单词和模式,使用密码管理器,并保护输出信息安全,以增强系统安全性。
|
2天前
|
Web App开发 运维 监控
深入探索Linux命令pwdx:揭秘进程工作目录的秘密
`pwdx`命令在Linux中用于显示指定进程的工作目录,基于`/proc`文件系统获取实时信息。简单易用,如`pwdx 1234`显示PID为1234的进程目录。结合`ps`和`pgrep`等命令可扩展使用,如查看所有进程或特定进程(如Firefox)的目录。使用时注意权限、进程ID的有效性和与其他命令的配合。查阅`man pwdx`获取更多帮助。
|
1天前
|
Linux 数据处理
探索Linux下的readlink命令:解析符号链接的利器
`readlink`命令在Linux中用于揭示符号链接的指向,显示它们所链接的实际文件或目录的路径。它可以显示简洁的绝对路径(-f),处理循环链接(-e),或不加换行符输出(-n)。例如,查看`link.txt`指向:`readlink link.txt`;获取绝对路径:`readlink -f link.txt`。使用时要注意链接是否存在、权限问题和可能的循环链接。