socket之send与发送缓冲区大小的关系

     关于send函数在发送的数据长度大于发送缓冲区大小,或者大于发送缓冲区剩余大小时,socket会怎么反应。参见这篇博客的两种说法http://blog.csdn.net/gukesdo/article/details/7295592

自己做了个测试,服务器只起socket在侦听,不recv, 也不send.

//ubuntu10.04 32bit

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
int main(void)
{
    int fd;
    struct sockaddr_in addr;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(103);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    listen(fd,5);
}

客户端,将发送缓冲区大小设置成2k,然后一次发送3k的数据。

//ubuntu10.04 32bit

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
    int fd,ret,tmp,sendlen;
    struct sockaddr_in addr;
    char *buf;
    int sendBufLen = 1024*2;
    socklen_t optlen = sizeof(int);

    buf = (char *)malloc(1024 * 3);	
    fd = socket(AF_INET, SOCK_STREAM,0 );

    setsockopt(fd,SOL_SOCKET, SO_SNDBUF,(const char*)&sendBufLen, sizeof(int));
    getsockopt(fd,SOL_SOCKET, SO_SNDBUF,(int *)&tmp, &optlen);
    printf("send_tmp=%d,optlen=%d\n",tmp,(int)optlen);    //设置发送缓冲区2048
   
    getsockopt(fd,SOL_SOCKET, SO_RCVBUF,(int *)&tmp, &optlen);
    printf("recv_tmp=%d,optlen=%d\n",tmp,(int)optlen);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(103);
    addr.sin_addr.s_addr = inet_addr("222.111.112.204"); //填上自己的IP
    
    ret = connect (fd, (struct sockaddr *)&addr, sizeof(addr));
    printf("connect return %d\n",ret);
    getchar();
    if (ret >= 0)
	sendlen = send(fd,buf,1024*3,0);
    printf("sendlen=%d\n",sendlen);       //此处返回3072
    getchar();

    return 0;
}

交互报文


从这里看出当发送长度大于缓冲区大小时,是分次发送,三次加起来 1448 + 1448 + 176 = 3072

在windows下,做同样的测试

// xp vc6.0 32bit

#include <stdio.h>
#include <winsock2.h>
#include <iostream.h>

#pragma  comment (lib,"ws2_32")

void  main()
{    
	
	int         len,optval,optlen,iResult;
	SOCKADDR_IN clientService;// 地址
	SOCKET      ConnectSocket;// socket
	WSADATA     wsaData;// 库
	unsigned char buf[1024*3];	
	
	//初始化socket库, 保存ws2_32.dll已经加载
	iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
	if (iResult != NO_ERROR)
		printf("Error at WSAStartup()\n");
	
	// 创建socket
	ConnectSocket = socket(AF_INET, // IPv4
		SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信
		IPPROTO_TCP  // 使用TCP协议
		/*0*/);
		
		if (ConnectSocket == INVALID_SOCKET)
		{
			printf("Error at socket(): %d\n", WSAGetLastError());
			WSACleanup();
			return ;
		}

		optlen = sizeof(optval);
		getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);
		printf("send buf len is %d\n",optval);           //默认发送缓冲区8k
		getsockopt(ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);
		printf("Recv buf len is %d\n",optval);          //默认接收缓冲区8k

		optval = 1024 * 2;
		setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);
		getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);
		printf("send buf len change to %d\n",optval);

		// 设置服务端的通信协议、IP地址、端口
		clientService.sin_family = AF_INET;
		clientService.sin_addr.s_addr = inet_addr( "222.111.112.204" );
		clientService.sin_port = htons( 103 );
		
		// 连接到服务端
		if ( connect(
			ConnectSocket, // socket
			(SOCKADDR*) &clientService, // 地址
			sizeof(clientService) // 地址的大小
			) == SOCKET_ERROR)
		{
			printf( "Failed to connect(%d)\n",WSAGetLastError() );
			WSACleanup();
			return ;
		}		
		
		len = send(ConnectSocket, (char*)buf, 1024*3, 0);
		printf("send length is %d\n",len);             //这里同样直接返回3072
		system("pause");
		return;	
}
抓取报文

分三次发送1460+1460+152 = 3072

上面都是阻塞的发送,对于socket是非阻塞的话,做同样的测试

//ubuntu10.04 32bit

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main()
{
    int fd,ret,tmp,sendlen,flags,fd_num;
    fd_set  rset,wset;
    struct timeval tval;
    struct sockaddr_in addr;
    char *buf;
    int sendBufLen = 1024*2;
    socklen_t optlen = sizeof(int);

    buf = (char *)malloc(1024 * 3);	
    fd = socket(AF_INET, SOCK_STREAM,0 );

    setsockopt(fd,SOL_SOCKET, SO_SNDBUF,(const char*)&sendBufLen, sizeof(int));
    getsockopt(fd,SOL_SOCKET, SO_SNDBUF,(int *)&tmp, &optlen);
    printf("send_tmp=%d,optlen=%d\n",tmp,(int)optlen);
   
    getsockopt(fd,SOL_SOCKET, SO_RCVBUF,(int *)&tmp, &optlen);
    printf("recv_tmp=%d,optlen=%d\n",tmp,(int)optlen);
	
    flags = fcntl(fd,F_GETFL,0);
    fcntl(fd,F_SETFL,flags|O_NONBLOCK);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(103);
    addr.sin_addr.s_addr = inet_addr("222.111.112.204"); 
	
    ret = connect (fd, (struct sockaddr *)&addr, sizeof(addr));

    if ((ret < 0) && (errno == EINPROGRESS))
    {
		FD_ZERO(&rset);
		FD_SET(fd,&rset);
		wset = rset;
		tval.tv_sec = 3;
		tval.tv_usec = 0;

		if ( -1 == (fd_num = select(fd+1, &rset, &wset, NULL, &tval)))
		{
			perror("select error");
			exit(0);
		} 
                else
		{
			if ((fd_num == 1) && FD_ISSET(fd, &wset))  //connect only can be write
			{
				printf("connect OK!\n");
			}
			else
			{
				perror("state error");
				exit(0);
			}
		}	
    }

    if (-1 ==(sendlen = send(fd,buf,1024*3,0)))
    {
	perror("send error");
    } 
    else
    {
	printf("sendlen=%d\n",sendlen);	  //直接返回3072
    }		
    return 0;
}

和blocking socket表现是一样的,一次send,协议栈分三帧发送。

//xp vc6.0 32bit

#include <stdio.h>
#include <winsock2.h>
#include <iostream.h>

#pragma  comment (lib,"ws2_32")

void  main()
{    	
	int	len,optval,optlen,iResult,fd_num,on=1;
	SOCKADDR_IN clientService;// 地址
	SOCKET      ConnectSocket;// socket
	WSADATA     wsaData;// 库
	unsigned char buf[1024*3];//
	fd_set		rset,wset;
	struct timeval tval;	
	
	//初始化socket库, 保存ws2_32.dll已经加载
	iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
	if (iResult != NO_ERROR)
		printf("Error at WSAStartup()\n");
	
	// 创建socket
	ConnectSocket = socket(AF_INET, // IPv4
		SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信
		IPPROTO_TCP  // 使用TCP协议
		/*0*/);
		
		if (ConnectSocket == INVALID_SOCKET)
		{
			printf("Error at socket(): %d\n", WSAGetLastError());
			WSACleanup();
			return ;
		}
		
		if(0 == ioctlsocket(ConnectSocket, FIONBIO, (unsigned long *)&on))
		{
			printf("socket non-blocking set success!\n");
		}
		
		optlen = sizeof(optval);
		getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);
		printf("send buf len is %d\n",optval);
		getsockopt(ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);
		printf("Recv buf len is %d\n",optval);
		
		optval = 1024 * 2;
		setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);
		getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);
		printf("send buf len change to %d\n",optval);
		
		// 设置服务端的通信协议、IP地址、端口
		clientService.sin_family = AF_INET;
		clientService.sin_addr.s_addr = inet_addr( "222.111.112.204" );
		clientService.sin_port = htons( 103 );
		
		// 连接到服务端
		if ( connect(
			ConnectSocket, // socket
			(SOCKADDR*) &clientService, // 地址
			sizeof(clientService) // 地址的大小
			) == SOCKET_ERROR)
		{
			if ( WSAEWOULDBLOCK == WSAGetLastError() ) 
			{
				FD_ZERO(&rset);
				FD_SET(ConnectSocket,&rset);
				wset = rset;
				tval.tv_sec = 3;
				tval.tv_usec = 0;
				if (SOCKET_ERROR == (fd_num = select(ConnectSocket+1, &rset, &wset, NULL, &tval)))
				{
					printf( "select Failed (%d)\n",WSAGetLastError() );
					WSACleanup();
					return ;
				} 
				else
				{
					if ((fd_num == 1) && FD_ISSET(ConnectSocket, &wset))  //connect成功后,只能可写,不能可读可写
					{
						printf("connect OK!\n");
					}
					else
					{
						printf( "connect Failed (%d)\n",WSAGetLastError() );
						WSACleanup();
						return ;
					}			
				}
			} 
			else
			{
				printf( "Failed to connect(%d)\n",WSAGetLastError() );
				WSACleanup();
				return ;
			}			
		}			
		
		if (SOCKET_ERROR == (len=send(ConnectSocket, (char*)buf, 1024*3, 0)))
		{
			printf("send error %d\n", WSAGetLastError());
		} 
		else
		{
			printf("send length is %d\n",len);  //直接返回3072
		}
		
		system("pause");
		return;	
}


       可见,当send的数据长度大于socket的缓冲区长度时,不管是windows还是linux,send都会分帧发送。







发布了21 篇原创文章 · 获赞 7 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览