作者: Sam (甄峰) sam_code@hotmail.com
socket在发送和接收时,根据不同的协议族和Type,会有不同类型的缓冲区。现研究如下。
1. TCP socket的接收和发送缓冲区:
socket(AF_INET, SOCK_STREAM, 0);
每个TCP Socket在内核中都有一个发送缓冲区和一个接收缓冲区,
TCP的全双工工作模式以及TCP的滑动窗口就是依赖这两个独立的buufer以及Buffer的填充状态。
对端发送过来数据,内核把数据缓存入接收缓冲区,应用程序一直没有调用read()读取的话,此数据会一直缓存在相应的socket的接收缓冲区。
若应用程序调用read(),会把接收缓冲区的数据读取用应用程序层的buffer.
应用程序调用write()或send()时,仅仅是把buffer中的数据copy到socket的发送缓冲区中。write()或send()返回时,data并不一定已经发送到对端了。
如果应用进程一直没有读取,buffer满了之后,发生的动作是:通知对端TCP协议中的窗口关闭。这个便是滑动窗口的实现。保证TCP套接口接收缓冲区不会溢出,从而保证了TCP是可靠传输。因为对方不允许发出超过所通告窗口大小的数据。
这就是TCP的流量控制,如果对方无视窗口大小而发出了超过窗口大小的数据,则接收方TCP将丢弃它。
int rcvbuf =
0;
int sndbuf = 0;
socklen_t optlen;
int ret_opt = 0;
strcpy(buf,
send_buf);
// 1. socket
iSocket_Server =
socket(AF_INET, SOCK_STREAM, 0);
if(iSocket_Server ==
-1)
{
perror("socket()");
return -1;
}
// 1.4: Get socket send and
receive buffer size
optlen =
sizeof(sndbuf);
ret_opt =
getsockopt(iSocket_Server, SOL_SOCKET, SO_SNDBUF, &sndbuf,
&optlen);
if(ret_opt == 0)
printf("\nSend buffer
length: %d\n", sndbuf);
ret_opt =
getsockopt(iSocket_Server, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
&optlen);
if(ret_opt == 0)
printf("\nRecv buffer
length: %d\n", rcvbuf);
获取的结果是:
Send Buffer length:
16384
Recv Buffer Length:
87380
但Sam疑惑的是,从TCP
Socket一段发送,另一端不Read,为何竟然可以发送3579904byte???
这与Recv Buffer
大小并不相符。
2. UDP的接收缓冲区:
每个UDP
Socket都有一个接收缓冲区,没有发送缓冲区。有数据就直接发送,不管对方是否能够正确接收,也不管对端接收缓冲区是否已经满了。
UDP是没有流量控制的;快的发送者可以很容易地就淹没慢的接收者,导致接收方的UDP丢弃数据报。