应用进程写数据到TCP套接口
每一个TCP套接口都有一个发送缓冲区,我们可以用SO_SNDBUF套接口选项来改变这一缓冲区的大小。当应用进程调用系统函数write时,内核从应用进程的缓冲区中拷贝所有数据到套接口的发送缓冲区。如果套接口的发送缓冲区容不下应用程序的所有数据(或是应用进程的缓冲区大于套接口发送缓冲区,或是套接口发送缓冲区还有其他数据),应用进程将被挂起(睡眠)。这里假设套接口是阻塞的,这是通常的缺省设置。内核将不从write系统调用返回,直到应用进程缓冲区中的所有数据都
拷贝到套接口发送缓冲区。
所以从写一个TCP套接口的write调用成功返回仅仅表示我们可以重新使用应用进程的缓冲区。它并不告诉我们对方的TCP或对方应用进程已接收到数据。
TCP取套接口发送缓冲区的数据并把它发送给对方TCP,其过程基于TCP数据传送的所有规则。对方TCP必须确认收到数据,即只有收到对方的ACK,本方TCP才能删除套接口发送缓冲区中已确认的数据。TCP必须保留数据拷贝直到对方确认止。
- 取出套接口发送缓冲区中的数据组成分节;
TCP给每个数据块安上TCP头部(20个字节大小)以构成分节,并发送给IP。其中分节大小必须小于MSS(最大分节大小),MSS是由对方通告的,当对方未通告时就用536这个值。
- 取出分节数据组成数据报;
IP给每个TCP分节安上IP头部(IPV4为20个字节大小,IPV6为40个字节大小)以构成数据报,然后查找其目的IP地址的路由表项以确定外出接口,然后把数据报传递给相应的数据链路。IP可能先将数据报分片,再传送给链路层。但如上所述,MSS选项
的目的是避免分片,而新的实现又使用路径MTU(最大传输单元)发现功能。
- 取出数据报交由链路层传送
每一个链路有一个输出队列,如果输出队列满,则分组丟弃,并沿协议栈向上返回一个错误:从链路层到IP层,再从IP层到TCP层。TCP将注意到这个错误,并在以后某个时刻重传这个分节。应用进程并不知道这种暂时情况。
应用进程写数据到UDP套接口
刚才讨论了TCP套接口从发送缓冲区中取得数据,但在我们UDP套接口中并没有物理上的发送缓冲区,但为什么还有这个概念呢?UDP套接口有发送缓冲区大小的原因,仅仅是说明应用程序能写到套接口的UDP数据报的大小上限。如果应用进程写一大于套接口发送缓冲区的数据报,则返回EMSGSIZE错误。因为UDP是不可靠的,它不必保存应用进程的数据拷贝,因此无需一个真正的发送缓冲区。(应用进程的数据在沿协议栈向下传递时,以某种形式拷贝到内核的缓冲区,然而当链路层把数据传出后这个拷贝就丢弃。)
- 取出应用进程数据组成UDP数据报;
UDP简单地安上它的8个字节的头部以构成数据报并把它传递给IP。
2.取出数据报组成分组;
IPv4 或IPv6给它安上相应的IP头部,执行路由操作确定外出接口,然后或者直接把数据报加入链路层输出队列(如果适合于MTU),或是分片后再把每个片段加入数据链路层的输出队列。