TCP、UDP的输出

TCP输出

每一个TCP套接字都有一个发送缓冲区。可以使用套接字选项SO_SNDBUF来更改该缓冲区的大小。当某个进程调用write时,内核从该应用进程的缓冲区中复制所有数据到所写的套接字缓冲区中。

(1)写入

如果套接字缓冲区容量不够,该应用进程会进入睡眠。此时内核不会从wirte系统调用返回,直到应用进程的数据全部写完。write调用返回成功,说明原来的应用进程缓冲区可以重新使用了。这与UDP不同。UDP的write返回成功,证明数据已经加入输出队列。

(2)封装发送

TCP提取套接字缓冲区中的数据准备发给对端TCP。在发送时,TCP根据MSS大小封装成TCP数据报。MSS值由对端通知或者默认值536。这个TCP数据报被加上IP首部封装成IP数据报,并按照目的地IP地址发送出去。由于MSS的存在,一般不会将IP数据报进行分片。

(3)确认和丢弃

一直到收到对端TCP的确认ACK后,本地TCP才会丢弃发送缓冲区中已经确认的数据。
TCP、UDP的输出

UDP输出

(1)写入

UDP套接字也有发送缓冲区,但是这个缓冲区仅仅作为能够写道该套接字的UDP数据报的大小上限。如果一个应用进程写进的数据超出这个套接字缓冲区的大小,内核会返回一个EMSGSIZE错误(不像TCP会进入阻塞直到全部写完)。这个缓冲区不会保留数据,发完即丢。

(2)封装和发送

UDP给数据报加上首部构成UDP数据报,然后传给IP。IP再将UDP数据报封装成IP数据报,传给链路输出队列。UDP相比TCP更容易被分片。

(3)调用成功

应用进程调用write如果返回成功,说明所写的数据报已经被加入到数据输出队列(这与TCP不同)。如果失败,可能会返回一个ENOBUFS错误(不是每个UDP都有错误返回。)
TCP、UDP的输出