技术笔记: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

相关文章
|
22天前
|
存储 安全 Linux
|
2月前
|
Ubuntu Linux Python
Tkinter错误笔记(一):tkinter.Button在linux下出现乱码
在Linux系统中,使用Tkinter库时可能会遇到中文显示乱码的问题,这通常是由于字体支持问题导致的,可以通过更换支持中文的字体来解决。
133 0
Tkinter错误笔记(一):tkinter.Button在linux下出现乱码
|
24天前
|
Linux Shell 数据安全/隐私保护
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
94 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
Linux 编译器 C语言
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
|
2月前
|
网络协议 Linux 网络性能优化
Linux C/C++之TCP / UDP通信
这篇文章详细介绍了Linux下C/C++语言实现TCP和UDP通信的方法,包括网络基础、通信模型、编程示例以及TCP和UDP的优缺点比较。
38 0
Linux C/C++之TCP / UDP通信
|
2月前
|
Linux 虚拟化
Vmware 傻瓜式安装(不可不知道的Linux基础知识和技术 01)
本文介绍了VMware虚拟机的下载与安装步骤。首先,通过提供的网盘链接下载VMware安装包。接着,详细描述了安装流程,包括接受协议、选择安装路径(建议避免系统C盘)、取消更新选项等。最后,输入许可证密钥完成安装,并展示了打开虚拟机后的主界面。整个过程简单易懂,适合新手操作。
145 1
|
2月前
|
网络协议 Linux
linux学习之套接字通信
Linux中的套接字通信是网络编程的核心,允许多个进程通过网络交换数据。套接字提供跨网络通信能力,涵盖本地进程间通信及远程通信。主要基于TCP和UDP两种模型:TCP面向连接且可靠,适用于文件传输等高可靠性需求;UDP无连接且速度快,适合实时音视频通信等低延迟场景。通过创建、绑定、监听及读写操作,可以在Linux环境下轻松实现这两种通信模型。
39 1
|
2月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
124 1
|
2月前
|
Linux 开发工具
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
下一篇
无影云桌面