TCP协议发送和接收数据报的过程还是相当的复杂的,这里,我们在完成TCP三次握手协议后,试图避开各种复杂的因素,介绍一个最为简单的TCP数据报发送流程。
跟UDP,RAW类似,函数mytcp_sendmsg负责发送TCP数据报。首先,要确定最大报文段(MSS)的长度,因为TCP需要把应用数据分割成它认为最为合适的数据块大小,然后传给IP,而不需要IP层再去进行数据报分割,我们得到的MSS是1448。
接下来我们要进行应用数据的分割,关于应用数据的结构体struct msghdr *msg的介绍,请查阅前面的文章,这里不再重复。
结构体struct sock有两个成员,sk_write_queue是一个发送队列,有一个或多个待发送的socket缓冲区struct
sk_buff排队。sk_send_head指向发送队列中当前需要发送的第一个strcut
sk_buff。如果sk_send_head为NULL,则表示当前队列为空,我们需要构建一个新的struct
sk_buff来发送我们的应用数据,或者,如果sk_write_queue中最后一个struct
sk_buff的长度已经达到MSS的值,则我们也需要构建一个新的struct
sk_buff,否则我们可以把新的应用数据添加到sk_write_queue队列中最后一个struct sk_buff中。
如果要构建新的strcut sk_buff以发送数据,我们首先检查发送缓存是否还有足够的空间,struct
sock的成员sk_wmem_queued记录了当前在发送缓存队列sk_write_queue中总的字节数,而成员sk_sndbuf是缓存队列大
小的上限值,如果当前缓存数据已经达上限,则不能再开僻新的套接字缓冲区,必须等待。新创建成功的struct
sk_buff加入到sk_write_queue的队列尾,然后把应用数据部分或全部拷到该strcut sk_buff中。
如果这是本次send系统调用创建的第一个strcut
sk_buff,则需要置标志位PSH,表示接收方应该尽快将这个报文段交给应用层。接下来还要更新一些参数,struct
tcp_sock的成员write_seq记录的是发送缓冲队列中的最后一个序号,需要加上本次发送的长度,
完成了一个strcut
sk_buff的创建后(或者是在队列中的最后一个sk_buff上添加数据),如果发现,当前队列中最后一个strcut
sk_buff的长度还没有达到MSS,则我们继续处理应用数据。如果当前缓冲队列中的最新序号跟发送序号(已经真正发送出去的最后一个序号,由
struct
tcp_sock的成员pushed_seq记录)之间的差值大于半个最大滑动窗口的大小,即发送缓冲队列中的的数据已经达到或超过半个滑动窗口了,则必
须先发送一次,再继续处理应用数据。因为滑动窗口是对端通告的本次能一次接收的最大数据量,如果缓冲队列中缓存的数据量太大,超过滑动窗口的大小了,则一
次发送出去,会造成对端的接收有问题。
发送数据前,先更新struct
tcp_sock的成员puashed_seq为write_seq,因为发送时,肯定是要把缓冲队列中的数据全部发送完的,同时,置本次发送的最后一个
数据报的PSH标志。另外,对于一些小数据量,即本次应用数据处理完后,缓存队列中只有一个strcut sk_buff,则也立即发送出去。
实际的发送处理还会遇到如阻塞算法等复杂问题,暂时不作介绍。
阅读(3944) | 评论(0) | 转发(1) |