每个TCP socket
在内核中都有一个发送缓冲区和一个接收缓冲区。
接收缓冲区把数据缓存入内核,应用进程一直没有调用read
进行读取的话,此数据会一直缓存在相应socket
的接收缓冲区内。不管进程是否读取socket
,对端发来的数据都会经由内核接收并且缓存到socket
的内核接收缓冲区之中。read
所做的工作,就是把内核缓冲区中的数据拷贝到应用层用户的buffer
里面。
接收缓冲区被TCP
和UDP
用来缓存网络上来的数据,一直保存到应用进程读走为止。对于TCP
,如果应用进程一直没有读取,buffer
满了之后,发生的动作是:通知对端TCP
协议中的窗口关闭。这个便是滑动窗口的实现。保证TCP
套接口接收缓冲区不会溢出,从而保证了TCP
是可靠传输。如果对方无视窗口大小而发出了超过窗口大小的数据,则接收方TCP
将丢弃它。
进程调用send
发送的数据的时候,最简单情况,将数据拷贝进入socket
的内核发送缓冲区之中,然后send
便会在上层返回。也就是说,send
返回之时,数据不一定会发送到对端去(和write
写文件有点类似),send
仅仅是把应用层buffer
的数据拷贝进socket
的内核发送buffer
中。
每个UDP socket
都有一个接收缓冲区,没有发送缓冲区,从概念上来说就是只要有数据就发,不管对方是否可以正确接收,所以不缓冲,不需要发送缓冲区。
由于TCP是流式的,对于TCP而言,每个TCP连接只有syn开始和fin结尾,中间发送的数据是没有边界的,多个连续的send所干的事情仅仅是:
- 假如socket的文件描述符被设置为阻塞方式
,而且发送缓冲区还有足够空间容纳这个send所指示的应用层buffer的全部数据,那么把这些数据从应用层的buffer,拷贝到内核的发送缓冲区,然后返回
- 假如socket的文件描述符被设置为阻塞方式
,但是发送缓冲区没有足够空间容纳这个send所指示的应用层buffer的全部数据,那么能拷贝多少就拷贝多少,然后进程挂起,等到TCP对端的接收缓冲区有空余空间时,通过滑动窗口协议通知TCP本端:“我已经做好准备,您现在可以继续向我发送X个字节的数据了”,然后本端的内核唤醒进程,继续向发送缓冲区拷贝剩余数据,并且内核向TCP对端发送TCP数据,如果send所指示的应用层buffer中的数据在本次仍然无法全部拷贝完,那么过程重复。。。直到所有数据全部拷贝完,返回。后续发送过程中,接收端会不断的用ACK通知发送端自己的接收窗口的大小状态;而发送数据的量,就根据这个接收窗口的大小来确定,发送端不会发送超过接收端能力的数据量,这样就起到了一个流量控制的作用。
- 假如socket的文件描述符被设置为非阻塞方式
,而且发送缓冲区还有足够空间容纳这个send所指示的应用层buffer的全部数据,那么把这些数据从应用层的buffer拷贝到内核的发送缓冲区,然后返回
- 假如socket的文件描述符被设置为非阻塞方式
,但是发送缓冲区没有足够空间容纳这个send所指示的应用层buffer的全部数据,那么能拷贝多少就拷贝多少,然后返回拷贝的字节数。