tcp协议系列文章(7):send()的数据大小与可用的发送缓冲区大小的关系
2017-06-23 10:48
405 查看
笔者这里要指出的是,man send 手册上说的或许与send()的版本有关。详细的,可以查看笔者的另一篇博客,上面有就send()的行为的详细说法。
tcp协议系列文章(6):send
下面的博客内容,其实验证的方法与验证的目的并不相同!!!请读者注意!!!
本博客原始地址在http://blog.csdn.net/smart55427/article/details/9112941 对开发者表示感谢。
关于send函数在发送的数据长度大于发送缓冲区大小,或者大于发送缓冲区剩余大小时,socket会怎么反应。参见这篇博客的两种说法http://blog.csdn.net/gukesdo/article/details/7295592
自己做了个测试,服务器只起socket在侦听,不recv, 也不send.
[cpp]
view plaincopy
//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的数据。
[cpp]
view plaincopy
//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下,做同样的测试
[cpp]
view plaincopy
// 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是非阻塞的话,做同样的测试
[cpp]
view plaincopy
//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,协议栈分三帧发送。
[cpp]
view plaincopy
//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都会分帧发送。
tcp协议系列文章(6):send
下面的博客内容,其实验证的方法与验证的目的并不相同!!!请读者注意!!!
本博客原始地址在http://blog.csdn.net/smart55427/article/details/9112941 对开发者表示感谢。
关于send函数在发送的数据长度大于发送缓冲区大小,或者大于发送缓冲区剩余大小时,socket会怎么反应。参见这篇博客的两种说法http://blog.csdn.net/gukesdo/article/details/7295592
自己做了个测试,服务器只起socket在侦听,不recv, 也不send.
[cpp]
view plaincopy
//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的数据。
[cpp]
view plaincopy
//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下,做同样的测试
[cpp]
view plaincopy
// 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是非阻塞的话,做同样的测试
[cpp]
view plaincopy
//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,协议栈分三帧发送。
[cpp]
view plaincopy
//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都会分帧发送。
相关文章推荐
- socket之send与发送缓冲区大小的关系
- socket之send与发送区的大小关系
- tcp协议系列文章(4):TCP带宽,时延和RTT等的关系
- tcp协议系列文章(6):send
- 16.3.3 发送网络数据send()
- VB中采用TCP协议用winsock控件发送和接收自定义结构体数据
- 『C程序设计』读书笔记系列文章之第二章 数据类型、运算符与表达式
- MS Sql Server查询磁盘的可用空间,数据库数据文件及日志文件的大小及利用率
- Scott的ASP.net MVC框架系列文章之三: 视图数据的传递
- 全文检索、数据挖掘、推荐引擎系列5---文章术语向量表示法
- 深入理解SQLite3系列 (四)关系数据基础
- 怎样查询磁盘的可用空间,数据文件及日志文件大小及利用率
- 全文检索、数据挖掘、推荐引擎系列5---文章术语向量表示法
- Scott的ASP.net MVC框架系列文章之四:处理表单数据(2)
- 全文检索、数据挖掘、推荐引擎系列5---文章术语向量表示法
- 深入理解SQLite3系列 (四)关系数据基础
- SQL Server, 对于指定的缓冲区大小而言,源列的数据太大
- 关于翻译"asp.net2.0数据操作指南"系列文章的说明
- C# 视频监控系列(5):客户端——给服务器端发送字符串和录像(数据捕获)
- 数据窗口使用技巧系列文章--回车键的完整编程