博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
socket之send与发送缓冲区大小的关系
阅读量:4112 次
发布时间:2019-05-25

本文共 7578 字,大约阅读时间需要 25 分钟。

     关于send函数在发送的数据长度大于发送缓冲区大小,或者大于发送缓冲区剩余大小时,socket会怎么反应。参见这篇博客的两种说法

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

//ubuntu10.04 32bit#include 
#include
#include
#include
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 
#include
#include
#include
#include
#include
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 
#include
#include
#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 
#include
#include
#include
#include
#include
#include
#include
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 
#include
#include
#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都会分帧发送。

你可能感兴趣的文章
git命令速查表
查看>>
linux进程监控和自动重启的简单实现
查看>>
OpenFeign学习(三):OpenFeign配置生成代理对象
查看>>
OpenFeign学习(四):OpenFeign的方法同步请求执行
查看>>
OpenFeign学习(五):OpenFeign请求结果处理及重试控制
查看>>
OpenFeign学习(六):OpenFign进行表单提交参数或传输文件
查看>>
OpenFeign学习(七):Spring Cloud OpenFeign的使用
查看>>
Ribbon 学习(二):Spring Cloud Ribbon 加载配置原理
查看>>
Ribbon 学习(三):RestTemplate 请求负载流程解析
查看>>
深入理解HashMap
查看>>
XML生成(一):DOM生成XML
查看>>
XML生成(三):JDOM生成
查看>>
Ubuntu Could not open lock file /var/lib/dpkg/lock - open (13:Permission denied)
查看>>
collect2: ld returned 1 exit status
查看>>
C#入门
查看>>
查找最大值最小值
查看>>
C#中ColorDialog需点两次确定才会退出的问题
查看>>
数据库
查看>>
nginx反代 499 502 bad gateway 和timeout
查看>>
linux虚拟机安装tar.gz版jdk步骤详解
查看>>