C语言socket send()数据缓存问题

来源:互联网 发布:工业怪兽 知乎 编辑:程序博客网 时间:2024/05/17 03:02

send()函数默认情况下会使用Nagle算法,Nagle算法通过将未确认的数据存入缓冲区直到积攒到一定数量一起发送的方法,来减少主机发送零碎小数据包的数目。所以如果send()函数发送数据过快的话,该算法会将一些数据打包后统一发出去。如果不了接这种情况,接收端采会遇到看似很奇怪的问题,比如成功recv()的次数与成功send()的次数不相等,在这中情况下,接收端可以通过recv()的返回值是否为0来判断发送端是否发送完毕。

通过setsockopt()TCP_NODELAY选项来禁用Nagle算法。但经试验这种方法似乎不总是有效,不过对于相同数量的send(),设置了TCP_NODELAY选项后recv()成功的次数要比设置之前多出几倍。这是不是说明设置了TCP_NODELAY后,系统会进最大的努力不去缓存,但是如果send的实在太快的话,还是会缓存的。

因此,如果你不希望send()的数据被本地缓存到一定数量之后再发送,而是send()多少次就发送多少次,稳妥的方式还是每次send之后调用一下usleep()函数,给系统一个反应的时间。

下面的例子演示了send()调用的快慢对数据是否打包的影响,注释掉server里的usleep(1000),会导致数据打包发送。

TCP_NODELAY是唯一使用IPPROTO_TCP层的选项,宏TCP_NODELAY的头文件是linux/tcp.h或者netinet/tcp.h

因为不知到send()数据缓存的问题,我调试一天的程序,我的五一劳动节啊!!

server.c

#include<stdlib.h>#include<stdio.h>#include<string.h>#include<sys/types.h>#include<sys/socket.h>#include<sys/wait.h>#include<netinet/in.h>#include<netdb.h>#include<arpa/inet.h>#include<netinet/tcp.h>//#include<linux/tcp.h>int main(){int socksv, sockcl;struct sockaddr_in server_addr;struct sockaddr_in client_addr;int sin_size;if((socksv = socket(AF_INET, SOCK_STREAM, 0)) == -1){printf("sever socket fail\n");return -1;}memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(6001);server_addr.sin_addr.s_addr = INADDR_ANY;int r = 1;setsockopt(socksv, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(int));int t = 1;if(-1 == setsockopt(socksv, IPPROTO_TCP, TCP_NODELAY, &t, sizeof(int))){printf("setsockopt fail\n");return -1;}if(bind(socksv, (struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == -1){printf("server bind fail\n");return -1;}if(listen(socksv, 5) == -1){printf("server listen fail\n");return -1;}while(1){sin_size = sizeof(struct sockaddr_in);if((sockcl = accept(socksv, (struct sockaddr *)&client_addr, &sin_size)) == -1){printf("server accept fail\n");continue;}int times = 1024;int allbytes = 0;int i;for(i = 0; i < times; i++){char buf[] = "#this is a message from ptypeServer";int sendbytes;if((sendbytes = send(sockcl, buf, strlen(buf), 0)) == -1){printf("server send fail\n");}usleep(1000);allbytes += sendbytes;}printf("have send %d packages to client, allbytes=%d\n", times, allbytes);close(sockcl);}}


client.c

#include<stdlib.h>#include<stdio.h>#include<string.h>#include<sys/types.h>#include<sys/socket.h>#include<sys/wait.h>#include<netinet/in.h>#include<netdb.h>#include<arpa/inet.h>int main(){int socksv;char buf[1024 * 1024];if((socksv = socket(AF_INET, SOCK_STREAM, 0)) == -1){printf("socket fail\n");return -1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(6001);server_addr.sin_addr.s_addr = inet_addr("192.168.1.100");if(connect(socksv, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){printf("connect fail\n");return -1;}int times = 0;int allbytes = 0;int numbytes = 1;while(numbytes != 0){times++;if((numbytes = recv(socksv, buf, sizeof(buf), 0)) == -1){printf("recv fail\n");return -1;}//buf[numbytes] = '\0';//printf("numbytes=%d buf=[%s]\n", numbytes, buf);allbytes += numbytes;}printf("server is closed, have recv %d packages from server, ""allbytes=%d\n", times - 1, allbytes);}

参阅:http://baike.baidu.com/link?url=-QgA0U7iv5tno-qnorYKDMNazOeOdcGk-pKIVFcOy-n6vhoITKdzlCg1VZYjqJ1DnOlpaaA54E7HrqQX6Bc_e_



2 0