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

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
");
		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
");
		return -1;
	}
	if(bind(socksv, (struct sockaddr *)&server_addr,
			sizeof(struct sockaddr)) == -1){
		printf("server bind fail
");
		return -1;
	}
	if(listen(socksv, 5) == -1){
		printf("server listen fail
");
		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
");
			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
");
			}
			usleep(1000);
			allbytes += sendbytes;
		}
		printf("have send %d packages to client, allbytes=%d
", 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
");
		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
");
		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
");
			return -1;
		}
//		buf[numbytes] = '';
//		printf("numbytes=%d buf=[%s]
", numbytes, buf);
		allbytes += numbytes;
	}
	printf("server is closed, have recv %d packages from server, "
			"allbytes=%d
", times - 1, allbytes);
}

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



原文地址:https://www.cnblogs.com/tlnshuju/p/7135846.html