接收缓冲区的默认值 保存在/proc/sys/net/core/rmem_default
接收缓冲区的最大值 /proc/sys/net/core/rmem_max
发送缓冲区默认值 保存在/proc/sys/net/core/wmem_default
发送缓冲区的最大值 /proc/sys/net/core/rmem_max
实际接收缓冲区(最小值/默认值/最大值) /proc/sys/net/ipv4/tcp_rmem
实际接收缓冲区(最小值/默认值/最大值) /proc/sys/net/ipv4/tcp_wmem
新建立一个socket时,socket的默认发送和接收缓冲区的大小是从这两个文件获取然后设置的。用getsockopt获取到的值跟这两个文件一致。
recv()所做的工作,就是把socket内核缓冲区中的数据拷贝到应用层用户的buffer里面并返回。
send()所做的工作就是,将数据从用户层缓冲区拷贝进入socket的内核发送缓冲区之中。此时有两种情况:
+ 如果是阻塞模式
+ 如果用户层缓冲区数据没有发送完毕,则阻塞,知道所有数据被拷贝到内核缓冲区。
+ 如果已经拷贝完毕,则直接返回已经拷贝的字节数。
+ 如果是非阻塞模式,不论用户层缓冲区的数据是否已经完全拷贝到socket内核缓冲区,都会直接返回已经拷贝的数据大小。
send()只负责将数据拷贝,并不等待ACK,所以send返回时数据不一定已经发送到对端,只是保证了数据从应用层拷贝到socket内核缓冲区。tcp协议会保证数据的可靠交付。
TCP协议本身是保证可靠传输,但并不等于应用程序用tcp发送数据就一定是可靠的。
当网络上有数据发送过来时,内核会把数据存放到到socket的内核接收缓冲区之中。如果应用进程一直没有调用recv读取导致socket内核缓冲区满,则通知发端接收窗口关闭(win=0)。
通过滑动窗口,TCP保证了接收缓冲区不会溢出。如果对方无视窗口大小而发出了超过窗口大小的数据,则接收方TCP将丢弃它。
当接收窗口关闭时,send如果继续往socket缓冲区发送数据导致缓冲区满,内核缓冲区的数据又发送不出去,进而导致send被阻塞在应用层;
随着进程不断的用recv将数据从内核的接收缓冲区拷贝至应用层的buf,这个时候接收缓冲区又恢复缓存能力,接收端会主动发送携带”win=n”这样的ACK包去通告发送端接收窗口已打开。发端收到携带”win=n”这样的ACK包之后,开始继续在窗口允许的数据量范围内发送数据。
接收端的窗口,在刚调用recv读取数据时,由于慢启动,接收窗口是持续增大的(表明接受能力在增加,吞吐量持续上升)。之后便键入一个稳定的阶段。
TCP的滑动窗口大小实际上就是socket的接收缓冲区大小的字节数。
server端的socket一定要在listen之前设置缓冲区大小,因为,accept时新产生的socket会继承监听socket的缓冲区大 小。client端的socket一定要在connet之前设置缓冲区大小,因为connet时需要进行三次握手过程,会通知对方自己的窗口大小。在 connet之后再设置缓冲区,已经没有什么意义。
TCP如何保证数据的可靠交付
1.超时重传
2.接收确认
3.流量控制(滑动窗口)
4.差错校验
5.合理分片
UDP有接收缓冲区。但是由于没有流量控制,当发送端速度较快时可能会导致接收端较慢的数据被淹没进而被丢弃。
TCP具有流量控制,接收缓冲区不可能溢出,当接收缓冲区满时数据包就会被丢弃。
UDP由于是不可靠的,并没有真正的发送缓冲区,UDP只是以某种形式将用户数据拷贝到内核缓冲区。UDP只有一个发送缓冲区最大值。当一次往UDP套接口中写入的数据超过这个值,内核会返回EMSGSIZE错误。