本文共 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都会分帧发送。