TCP的粘包成绩以及数据的无界限性
上节我们讲到了socket缓冲区和数据的传递进程,可以看到数据的接纳和发送是有关的,read()/recv() 函数不论数据发送了若干次,都邑尽能够多的接纳数据。也就是说,read()/recv() 和 write()/send() 的履行次数能够分歧。例如,write()/send() 反复履行三次,每次都发送字符串"abc",那么目的机械上的 read()/recv() 能够分三次接纳,每次都接纳"abc";也能够分两次接纳,第一次接纳"abcab",第二次接纳"cabc";也能够一次就接纳到字符串"abcabcabc"。假定我们愿望客户端每次发送一位先生的学号,让效劳器端前往该先生的姓名、住址、成果等信息,这时分能够就会呈现成绩,效劳器端不克不及辨别先生的学号。例如第一次发送 1,第二次发送 3,效劳器能够当成 13 来处置,前往的信息显然是毛病的。这就是数据的“粘包”成绩,客户端发送的多个数据包被当做一个数据包接纳。也称数据的×××限性,read()/recv() 函数不晓得数据包的开端或完毕标记(实践上也没有任何开端或完毕标记),只把它们当做延续的数据流来处置。下面的代码演示了粘包成绩,客户端延续三次向效劳器端发送数据,效劳器端却一次性接纳到一切数据。效劳器端代码 server.cpp:
#include #include #pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll #define BUF_SIZE 100 int main(){ WSADATA wsaData; WSAStartup( MAKEWORD(2, 2), &wsaData); //创立套接字 SOCKET servSock = socket(AF_INET, SOCK_STREAM, 0); //绑定套接字 sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); //每一个字节都用0填充 sockAddr.sin_family = PF_INET; //运用IPv4地址 sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //详细的IP地址 sockAddr.sin_port = htons(1234); //端口 bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //进入监听形态 listen(servSock, 20); //接纳客户端恳求 SOCKADDR clntAddr; int nSize = sizeof(SOCKADDR); char buffer[BUF_SIZE] = {0}; //缓冲区 SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize); Sleep(10000); //留意这里,让程序暂停10秒 //接纳客户端发来的数据,并原样前往 int recvLen = recv(clntSock, buffer, BUF_SIZE, 0); send(clntSock, buffer, recvLen, 0); //封闭套接字并终止DLL的运用 closesocket(clntSock); closesocket(servSock); WSACleanup(); return 0; }
客户端代码 client.cpp:
#include #include #include #include #pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll #define BUF_SIZE 100 int main(){ //初始化DLL WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //向效劳器提议恳求 sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); //每一个字节都用0填充 sockAddr.sin_family = PF_INET; sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sockAddr.sin_port = htons(1234); //创立套接字 SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //获取用户输出的字符串并发送给效劳器 char bufSend[BUF_SIZE] = {0}; printf("Input a string: "); gets(bufSend); for(int i=0; i<3; i++){ send(sock, bufSend, strlen(bufSend), 0); } //接纳效劳器传回的数据 char bufRecv[BUF_SIZE] = {0}; recv(sock, bufRecv, BUF_SIZE, 0); //输入接纳到的数据 printf("Message form server: %s\n", bufRecv); closesocket(sock); //封闭套接字 WSACleanup(); //终止运用 DLL system("pause"); return 0; }
先运转 server,再运转 client,并在10秒内输出字符串"abc",再等数秒,效劳器就会前往数据。运转后果如下:Input a string: abcMessage form server: abcabcabc本程序的症结是 server.cpp 第31行的代码Sleep(10000);,它让程序暂停履行10秒。在这段工夫内,client 延续三次发送字符串"abc",因为 server 被壅塞,数据只能聚积在缓冲区中,10秒后,server 开端运转,从缓冲区中一次性读出一切积存的数据,并前往给客户端。别的还需求阐明的是 client.cpp 第34行代码。client 履行到 recv() 函数,因为输出缓冲区中没无数据,所以会被壅塞,直到10秒后 server 传回数据才开端履行。用户看到的直不雅后果就是,client 暂停一段工夫才输入 server 前往的后果。client 的 send() 发送了三个数据包,而 server 的 recv() 却只接纳到一个数据包,这很好的阐明了数据的粘包成绩。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~