最近刚接触linux的编程,在pc机上编了两个socket通信的程序做测试,一个采用TCP的方式,另一个采用UDP的方式。不断增大传输数据包的大小,到180k大小时,UDP通信收不到包,阻塞在recvfrom(),而TCP方式仍然能正常工作。
对于用SOCK_DGRAM创建的socket来发送UDP包,假如发送的数据过大,linux kernel 下面的TCP/IP stack 会重新分成小包发送的,对于应用程序来说,不需要关心数据大小。
问题在于发送的数据大小超过了内核缓冲区的最大限制,这个限制好象是65535(默认情况下是64K)
通过使用函数setsockopt(ip_socket->socket.fd, SOL_SOCKET, SO_RCVBUF, &size, &len) ,改变接收缓冲区大小的属性。同理,setsockopt(ip_socket->socket.fd, SOL_SOCKET, SO_SNDBUF, &size, &len) 也可以改变发送缓冲区的大小。
#define RMEM_MAX (1024*200)
{
/* dma: 内核默认设置接收BUF为64K,工作在0!14MB/S的低速情况下,当速度大于19MB/S时LINUX就
就会丢包 可以更改 /proc/sys/net/core/rmem_max 这个文件.
可使用如下命令进行更改echo 200000 > /proc/sys/net/core/rmem_max.
*/
FILE *f;
char buf[80];
int rmax;
f = fopen("/proc/sys/net/core/rmem_max", "rw");
if (f) {
fread(buf, 1, sizeof(buf), f);
sscanf(buf, "%d", &rmax); //从rmax中读取一个int赋值给buf
fclose(f);
if (rmax == 65535) {
sprintf(buf, "%d", RMEM_MAX); //将NEW_RMEM_MAX以%d的方式输出到buf中
f = fopen("/proc/sys/net/core/rmem_max", "w");
fwrite(buf, 1, strlen(buf)+1, f);
fclose(f);
new_rmem_max = RMEM_MAX;
} else
new_rmem_max = rmax;
}
}
结合以上信息总结:
1.UDP方式传输一段完整数据的大小时受内核缓冲区限制的,比如在默认情况下缓冲区大小是64K,所以对于该情况下的socket接收或者发送方,最大可以一次性发送一个64K的数据。而对于socket底层的具体实现是将64K的数据拆分成N个小数据包,内部提供机制保证数据的可靠性,例如每个包的大小为1K,则分成64个包,从服务器端发送到客户端,客户端在本地缓冲区重新组成完整的数据提交给用户。底层的实现方式应该各不相同,但是对于上层结果都是一样的。如果超过了缓冲区大小则无法完成组包出现丢包等等现象。同理,对于200K则可以发送接收200K大小的数据。