lwip ---(十三)TCP建立与断开

  TCP部分是整个LWIP最庞大也是难理解的部分,其代码将近占了整个协议栈代码量的一半。参考的基本主线还是标准协议的TCP部分。

  TCP叫传输控制协议,它为上层提供一种面向连接的、可靠的字节流服务。TCP通过下面的一系列机制来提供可靠性应用数据被分割成TCP认为最适合发送的数据块;当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段,如果不能及时收到一个确认,将重发这个报文段当TCP收到发自TCP连接另一端的数据,它将发送一个确认,这个确认不是立即发送,通常将推迟几分之一秒;TCP将保持它首部和数据的检验和,如果收到段的检验和有差错,TCP将丢弃这个报文段并且不发送确认收,以使发送端超时并重发;I P数据报的到达可能会失序,因此TCP报文段的到达也可能会失序,如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层;I P数据报会发生重复,TCP的接收端必须丢弃重复的数据;TCP还能提供流量控制

  下图是TCP首部结构,若不计任选字段,其大小为20字节,与IP报首部大小相同。
在这里插入图片描述
  源端口号和目的端口号,用于标识发送端和接收端的应用进程。这两个值加上IP首部中的源IP地址和目的IP地址就能唯一确定一个TCP连接一个IP地址和一个端口号也称为一个插口(socket

  32位序号字段用来标识从TCP发送端到TCP接收端的数据字节流,用它来标识这个报文段中的第一个数据字节的序号当建立一个新的连接时,SYN标志置1,序号字段包含由这个发送主机选择的该连接上的初始序号ISN(Initial Sequence Number)。该主机要发送数据的第一个字节序号为ISN+1

  32位确认序号只有ACK标志为1时才有效,它包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已成功收到数据字节序号加 1当一个TCP连接被正确建立后,ACK字段总是被设置为1

  4位首部长度给出首部中32 bit字的数目。需要这个值是因为任选字段的长度是可变的。由于这个字段有4 bit,因此TCP最多有6 0字节的首部。然而,若没有任选字段,正常的长度是20字节

  在T C P首部中有6个标志比特。它们中的多个可同时被设置为 1。在这里简单介绍它们的用法,在以后用到时会详加讲解:URG紧急指针(urgent pointer)有效标识;ACK确认序号有效标识;PSH 接收方应该尽快将这个报文段交给应用层;RST重建连接;SYN同步序号,用来发起一个连接;FIN 发端完成发送任务。

  16位窗口大小字段通过声明自身的窗口大小来实现流量控制,窗口大小表示还能接收的字节数

  16位检验和覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,一定是由发送端计算和存储,并由收收端进行验证TCP检验和使用的TCP头部并不包括实际头部中的所有字段,而是一个伪首部,具体关于伪首部的结构可参看UDP部分,或参看协议。

  16位紧急指针是一个正的偏移量和序号字段中的值相加表示紧急数据最后一个字节的序号,只有当URG标志置1时紧急指针才有效。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。

  最常见的可选字段是最长报文大小,又称为 MSS (Maximum Segment Size)。每个连接方通常都在通信的第一个报文段(为建立连接而置 SYN标志的那个段)中指明这个选项。它指明本端所能接收的最大长度的报文段

  TCP报文段中的数据部分有时是为空的。例如在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP首部;又如一方没有数据要发送,则它需使用没有任何数据的首部来确认收到的数据;再在处理超时的许多情况中,也会发送不带任何数据的报文段。

  上面的都是协议规定的内容,没有任何自由发挥的空间,下面来看看LWIP是如何描述这样一个TCP报头的,数据结构tcp_hdr如下,与上图描述的完全相符,不解释了。

PACK_STRUCT_BEGIN
struct tcp_hdr {
    
    
	PACK_STRUCT_FIELD(u16_t src);                // 源端口
	PACK_STRUCT_FIELD(u16_t dest);               // 目的端口
	PACK_STRUCT_FIELD(u32_t seqno);              // 序号
	PACK_STRUCT_FIELD(u32_t ackno);              // 确认序号
	PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags); // 首部长度+保留位+标志位
	PACK_STRUCT_FIELD(u16_t wnd);                // 窗口大小
	PACK_STRUCT_FIELD(u16_t chksum);             // 校验和
	PACK_STRUCT_FIELD(u16_t urgp);               // 紧急指针
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

  先抛开LWIP的内容,仔细讲解详解卷中的内容。先讲讲一个TCP连接的建立过程。TCP建立连接需要有三个报文段的交互过程,所以又称三次握手过程。
在这里插入图片描述
  首先,请求端(通常称为客户)发送一个 SYN标志置1的TCP数据报,数据包中指明自己的端口号及将连接的服务器的端口号,同时通告自己的初始序号ISN

  当服务器接收到该数据包并解析后,也发回一个SYN报文段作为应答。该回应报文包含服务器自身选定的初始序号ISN,同时,ACK1将确认序号设置为请求端的ISN1以对客户的SYN报文段进行确认。这里的**ISN也表示了服务器希望接收到的下一个字节的序号**。由此可见,一个SYN将占用了一个序号

  最后,当请求端接收到服务器的SYN应答包后,会再次产生一个握手包,这个包中,ACK标志置位,确认序号设置为服务器发送的ISN1,以此来实现对服务器的SYN报文段的确认。

  通常存在这样的一种情况,两端同时发起连接,即同时发送第一个SYN数据包,这时,这两端都处于主动打开状态,在后续的讨论中我们将涉及这个内容。

  TCP连接的断开需要四次握手过程。一个TCP连接是全双工(即数据在两个方向上能同时传递),因此每个方向必须单独地进行关闭。当发送数据的一方完成它的数据发送任务后它就可以发送一个 FIN标志置1的握手包来终止这个方向连接。当另一端收到这个FIN包时,它必须通知应用层另一端已经终止了那个方向的数据传送。发送FIN通常是应用层进行关闭的结果,收到一个FIN意味着在这一方向上已经没有数据流动。一个TCP连接在收到一个FIN后仍能发送数据,此时的连接处于半关闭状态。通常首先进行关闭的一方(即发送第一个 FIN)将执行主动关闭,而另一方(收到这个FIN)执行被动关闭。通常一方完成主动关闭而另一方完成被动关闭,但也存在双方都为主动关闭的情况,这将在后续讨论。

  断开一个连接的四次握手过程如下图所示,首先客户端应用程序主动执行关闭操作时,客户端会向服务器发送一个FIN握手包,用来关闭从客户到服务器的数据传送。当服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。同时服务器还向其上层应用程序通告接收到结束动作,接着这个服务器程序就会关闭它的连接,导致它的TCP端发送一个FIN握手包,客户必须发回一个确认,并将确认序号设置为收到序号加1
在这里插入图片描述
  在这个图中,发送FIN将导致应用程序关闭它们的连接,这些FIN的ACK是由TCP软件自动产生的。连接断开的主动发起方通常是客户端,但如果是服务器首先发起FIN包,上图的握手过程也是完全成立的。

猜你喜欢

转载自blog.csdn.net/qq_40390825/article/details/113267685
今日推荐