TCP IP协议原理精讲

●抓包工具wireshark的安装:
1.linux下可以在终端中输入:sudo apt-get install wireshark(注意:在linux中运行wireshark的时候也要加上sudo,如:sudo wireshark,要不然可能抓不到包);
2.windos可以直接到官网下载进行安装;

●TCP/IP协议网络封包格式:
TCP IP协议原理精讲_第1张图片
●MTU: Maximum Transmit Unit,最大传输单元,即物理接口(数据链路层)提供给其上层(通常是IP层)最大一次传输数据的大小;以普遍使用的以太网接口为例,缺省MTU=1500 Byte,这是以太网接口对IP层的约束,如果IP层有<=1500 byte 需要发送,只需要一个IP包就可以完成发送任务;如果IP层有> 1500 byte 数据需要发送,需要分片才能完成发送,这些分片有一个共同点,即IP Header ID相同。

●MSS:Maximum Segment Size ,TCP提交给IP层最大分段大小,不包含TCP Header和 TCP Option,只包含TCP Payload,MSS是TCP用来限制application层最大的发送字节数。如果底层物理接口MTU= 1500 byte,则 MSS = 1500- 20(IP Header) -20 (TCP Header) = 1460 byte,如果application 有2000 byte发送,需要两个segment才可以完成发送,第一个TCP segment = 1460,第二个TCP segment = 540。

●网络报文中的payload:通常在传输数据时,为了使数据传输更可靠,要把原始数据分批传输,并且在每一批数据的头和尾都加上一定的辅助信息,比如数据量的大小、校验位等,这样就相当于给已经分批的原始数据加一些外套,这些外套起标示作用,使得原始数据不易丢失,一批数据加上“外套”就形成了传输通道的基本传输单元,叫做数据帧或数据包,而其中的原始数据就是payload。
下面是网络上的解释:
TCP报文一次性最大运输的货物量(Payload),大体可以这么来计算:
IP报文头长度 + TCP报文头长度 + Payload长度 ≤ MTU

即左边的三者之和,要小于等于右边MTU的长度,其中:

Internet 路由器接口标准MTU = 1500
IP报文头长度 = 20
TCP报文头长度 = 20

所以

Payload长度≤ MTU – IP报文头长度 – TCP报文头长度
≤ 1500 -20 -20
≤ 1460
但是TCP没有那么简单,所以还需要考虑得更多一点。

TCP报文通常有3部分组成:IP Header + TCP Header + Payload

但是,当前主流操作系统的TCP/IP协议栈,为了提高传输性能,通常还会使用 TCP Option选项。
一个例子

客户端在TCP握手连接,告诉服务器自己支持以下三个option:
(1) Maximum Segment Size
(2) SACK Permitted
(3) Timestamp

服务器接收到该报文,却有不同的意见,服务器只支持这三者中的:

(1) Maximum Segment Size
Option,翻译成中文是选项。所谓选项,不是强制标准,对方如果不支持或不理解,完全可以忽略。
在这里服务器并没有打算支持选项2 ,3, 所以双方共同支持双方的交集,即选项1。
Maximum Segment Size
MSS的存在是为了通信双方交换各自TCP Payload最大传输长度,这个长度上限一般为1460,即上文计算的方法。
如果双方的MSS不一样,将选择较小的MSS值,作为接下来通信Payload的长度上限。
假如服务器支持选项2,“Timestamp”,那么TCP报文将会包含4部分:
IP Header + TCP Header + TCP Option +Payload
这里的TCP Option为“Timestamp”,长度为 10字节,看看 Payload最大可以传输多少字节?

IP Header + TCP Header + TCP Option +Payload ≤ 1500
IP Header + TCP Header + Timestamp +Payload ≤ 1500
Payload ≤ 1500 - IP Header -TCP Header – Timestamp
≤ 1500 -20 -20 -10
≤ 1450

简而言之,没有携带option的TCP报文,最大可以支持1460字节的Payload长度。
一旦携带option,由于option需要占用空间,留给payload的空间将会相应减少,具体减少的空间等于option占用的空间。

Payload长度和Window Size有关系吗?
有一点点关系。

如果把Payload 1460看成一个标准的集装箱,Window Size可以看作双方的仓库大小,用于临时堆放对方运过来的集装箱,在集装箱被客户运走之前,一直会呆在TCP仓库里。

为了更高效地利用仓库,最理想的方法就是,仓库的大小是集装箱的整数倍,这样就不会产生零星的空间。

零星的空间一直无法使用,因为不够容纳一个标准集装箱,势必会造成仓库空间的浪费。
空间浪费只是直接后果,还有一个间接后果,更加严重。

假设接收方的仓库空间还有200字节,于是通过window size update消息告诉发送方。

发送方心急火燎发200字节,那么一个标准1460报文,将会分成8个小报文发送,这样的传输效率会非常低下。

为了避免这种低效传输场景,TCP协议有了新的严格规定:

如果 window size < MSS , 不允许更新自己的window。
上文用通俗语言表达为,一旦接收方的仓库空间小于一个标准集装箱,window size update = 0 , 即善意欺骗对方,仓库已经用完,不允许再发货物过来。

以上是从接收方入手,万一接收方没有遵守规定,那就让发送方严格执行另外一个规定。

发送方一旦发现对方的window size < MSS,理解为对方的仓库已经占满(剩余空间不足以容纳一个标准集装箱),不会发送任何集装箱。

●以太网包头(Ethernet V2 型帧)

TCP IP协议原理精讲_第2张图片

TCP IP协议原理精讲_第3张图片
TCP IP协议原理精讲_第4张图片
TCP IP协议原理精讲_第5张图片
TCP IP协议原理精讲_第6张图片
Not all well known de facto uses of EtherTypes are always recorded in the IEEE list of EtherType values.[11] For example, EtherType 0x0806 (used by ARP) appears in the IEEE list only as “Symbolics, Inc., Protocol unavailable.”[11] However, the IEEE Registration Authority lists all the accepted EtherTypes, including the 0x0806.[5]

https://en.wikipedia.org/wiki/EtherType

TCP IP协议原理精讲_第7张图片
·IPv4数据报。头部大小可变, 4位的IHL字段被限制为15个32位字(60字节)。一个典型的IPv4头部包含20字节(没有选项)。源地址和目的地址的长度为32位。第二个32位字的大部分用于IPv4分片功能。头部校验和有助于确保头部宇段被正确发送到目的地,.但不保护数据内容。

·版本号(Version):长度4bits。标识目前采用的IP协议的版本号。一般的值为0100(IPv4),0110(IPv6)

·IP包头长度(Header Length):包头长度:长度为4 bits,单位来是4个字节(32bits)。最大的值“1111”,能表示的数用十进制表示就是15(2^4-1),乘以单位4字节,来表示IP首部的长度,也就是最大154=60字节。注意,这里的单位“4字节(32bits)”对源于我一个业余的小白来说,想通这一点很费劲。“总共就只有4bits,怎么能单位是32bits呢?其实这里并不是真的留了4个单位32bits的物理长知度,而是我们人为规定的,是一种映射关系。比如说Header Length的值是:0101。那么转换成十进制就是5,并不是说IP首部的长度就是5,而是我们规定如道果这个位置的数值是5,我们就知道了其实代表的是54字节=20字节。

·服务类型(TOS;现在从TCP/IP详解卷一:协议 第二版中分为DS字段和ECN,跟现在不一样了):服务类型(TOS)字段包括一个3bit的优先权子字段(现在已被忽略),4bit的TOS子字段和1bit未使用位但必须置0。4bit的TOS分别代表:最小时延、最大吞吐量、最高可靠性和最小费用。4bit中只能置其中1bit。如果所有的4bit均为0,那么就意味着是一般服务。RFC1340[Reynolds and Postel 1992]描述了所有的标准应用如何设置这些服务类型。RFC 1349[Almquist 1992]对该RFC进行了修正,更为详细的描述了TOS的特性。
下图列出了对不同应用建议的TOS值,在最后一列中给出的是十六进制值,因为这就是在后面将要看到的tcpdump命令输出。
TCP IP协议原理精讲_第8张图片

Telnet和Rlogin这两个交互应用要求最小的传输时延,因为人们主要用他们来传输少量的交互数据,另一方面,FTP文件传输则要求有最大的吞吐量。最高可靠性被指明给网络管理(SNMP)和路由选择协议。用户网络新闻(Usenet news,NNTP)是唯一要求最小费用的应用。
现在大多数的TCP/IP实现都不支持TOS特性,但是自4.3BSD Reno以后的新版本系统都怼他进行了设置。另外,新的路由协议如OSPF和IS-IS都能根据这些字段的值进行路由决策。

·总长度(Total Length):总长度字段是IPv4数据包的总长度(以字节为单位)。通过这个字段和IHL字段,我们知道数据报的数据部分从哪里开始,以及他的长度。由于他是一个16位的字段,所以IPv4数据报的最大长度(包括头部)为65535字节。由于一些携带IPv4数据报的第层协议不能(精确)表达自己封装的数据报大小,所以需要在头部给出总长度字段。例如,以太网会将短帧填充到最小长度(64字节)。虽然以太网最小有效载荷为46字节,但一个IPv4数据报也可能会更小(20字节)。如果没有提供总长度字段,IPv4实现将无法知道一个46字节的以太网帧是一个IP数据报,还是经过填充的IP数据报,这样可能会导致混肴。

尽管可发送一个65535字节的IP数据报,但大多数链路层(例如以太网)不能携带这么大的数据,除非将它分(拆)成更小的片。另外,主机不需要接收大于576字节的IPv4数据报。(在IPv6中,主机需要能处理所连接链路MTU大小的数据报,而最小链路MTU为1280字节)很多使用UDP协议传输数据(例如DNS、DHCP等)的应用程序,限制为使用512字节大小的数据,以避免576字节的IPv4限制。TCP根据额外信息选择自己的数据报大小。

当一个IPv4数据报被分为多个更小的分片时,每个分片自身仍是一个独立的卫数据报,总长度字段反映具体的分片长度。IPv6头部不支持分片,其长度可由负载长度字段获得。这个字段提供IPv6数据报长度,不包括头部长度,但扩展头部包括在负载长度中。对于IPv4,这个16位的字段限制其最大值为65535。对于Pv6,负载长度被限制为64KB,而不是整个数据报。另外,IPv6还支持一个超长数据报选项,它至少在理论上提供了可能性,即单个分组的有效载荷可达到4GB(4294967295字节)!

·生存期(TTL)字段用于设置一个数据报可经过的路由器数量的上限。发送方将它初始化为某个值(RFCI12建议为4,但128或255也不少见),每台路由器在转发数据报时将该值减!当这个字段值达到0时,该数据报被丢弃。并使用一个ICMP消息通知发送方这可以防止由于出现不希望的路由环路而导致数据报在网络中永远循环。

注意:TTL字段最初指定IP数据报的最大生存期在几秒钟内,但路由器总需要将这个值至少减1,实际上,当前路由器在正常操作下通常不会持有数据报超过1秒钟,因此较早的规则现在已被忽略或遗忘。这个字在IPv6中根据实际用途已被重新命名为跳数限制。

·协议(Protocol):长度8比特。标识了上层所使用的协议。
以下是比较常用的协议号:
1 ICMP
2 IGMP
6 TCP
17 UDP
88 IGRP
89 OSPF

·头部校验(Header Checksum):长度16位。用来做IP头部的正确性检测,但不包含数据部分。 因为每个路由器要改变TTL的值,所以路由器会为每个通过的数据包重新计算这个值。

·起源和目标地址(Source and Destination Addresses):这两个地段都是32比特。标识了这个IP包的起源和目标地址。要注意除非使用NAT,否则整个传输的过程中,这两个地址不会改变。

·标识符(Identifier):长度16比特。该字段和Flags和Fragment Offest字段联合使用,对较大的上层数据包进行分段(fragment)操作。路由器将一个包拆分后,所有拆分开的小包被标记相同的值,以便目的端设备能够区分哪个包属于被拆分开的包的一部分。

·标记(Flags):长度3比特。该字段第一位不使用。第二位是DF(Don’t Fragment)位,DF位设为1时表明路由器不能对该上层数据包分段。如果一个上层数据包无法在不分段的情况下进行转发,则路由器会丢弃该上层数据包并返回一个错误信息。第三位是MF(More Fragments)位,当路由器对一个上层数据包分段,则路由器会在除了最后一个分段的IP包的包头中将MF位设为1。

·片偏移(Fragment Offset):长度13比特。表示该IP包在该组分片包中位置,接收端靠此来组装还原IP包。

下面是Identification IP Flags 和 Fragment offset的解释;
TCP IP协议原理精讲_第9张图片
①这里的id:0x8458指的就是Identification;当一个大包被分片后,这些分片的id是一样的;
②IP Flags中的x是保留位(没有用),D置一的话代表没有碎片化(D置一的情况通常都是在一个大的数据报被碎片化后的最后一个碎),M置一的话代表还又更多的碎片;
③Fragment Offset:Fragment offset from start of IP datagram. Measured in 8 byte(2 words, 64 bits) increments. If IP datagram is fragmented. fragment size(Total Length) must be a multiple of 8 bytes

TCP IP协议原理精讲_第10张图片
·TCP头部,它的标准长度是20字节,除非出现选项。头部长度(Header Length)字段以32位字为单位给出头部的大小(最小值是5)。带阴影的字段(确认号( Acknowledgment Number)、窗口大小( Window Size)以及ECE位和ACK位)用于与该报文段的发送方关联的相反方向上的数据流;

·每个TCP头部包含了源和目的端口号。这两个值与IP头部中的源和目的IP地址一起,唯一地标识了每个连接。在TCP术语中,一个IP地址和一个端口的组合有时被称为一个端点( endpoint)或套接字( socket)。后者出现在[RFC0793]中,最终被 Berkeley系列的网络通信编程接口所采用(现在经常被称为“ Berkeley套接字”)。每个TCP连接由一对套接字或端点(四元组,由客户机IP地址、客户机端口号、服务器IP地址以及服务器端口号组成)唯一地标识。这个事实在我们观察一个TCP服务器是如何做到与多个客户机通信的时候将会变得很重要。

·src port:源端口,2个字节,是一个大于1023的16位数字,由基于TCP应用程序的用户进程随机选择

·dst port:目的端口,2个字节,指明接收者所用的端口号,一般由应用程序来指定

·Sequence number:顺序号,4个字节,用来标识从 TCP 源端向 TCP 目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则 TCP 用顺序号对每个字节进行计数。序号是 32bit 的无符号数,序号到达 (2^32) - 1 后又从 0 开始。当建立一个新的连接时, SYN 标志变 1 ,顺序号字段包含由这个主机选择的该连接的初始顺序号 ISN ( Initial Sequence Number )

·Acknowledgement number:确认号,4个字节,包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1 。只有 ACK 标志为 1 时确认序号字段才有效

·Offset:报头长度,4位,给出报头中 32bit 字的数目。需要这个值是因为任选字段的长度是可变的。这个字段占 4bit , 即TCP 最多有 60(15*4) 字节的首部;
首部长度(数据偏移):该字段表示 TCP 所传输的数据部分应该从 TCP 包的哪个位置开始计算,可以把它看作是 TCP 首部的长度。该字段长 4 位,单位是 4 字节(即 32 位)。不包括选项字段的话,TCP 首部长度为 20 个字节,因此,数据偏移字段可设置为 5。反之,若该字段值为 5,那么说明从 TCP 包的一开始到 20 字节为止都是 TCP 首部,余下的部分为 TCP 数据;

·保留:该字段主要是为了以后扩展时使用,其长度为 4 位,一般设置为 0,但即使收到的包在该字段不为 0,此包也不会被丢弃;

·当前,为TCP头部定义了8位的字段,尽管一些老的实现只理解它们中的最后6位。它们中的一个或多个可被同时启用。我们在这里大致提一下它们的用法,在后面的几章里再对每个进行详细的讨论

CWR:CWR 标志与后面的 ECE 标志都用于 IP 首部的 ECN 字段,ECE 标志为 1 时,则通知对方已将拥塞窗口缩小;

ECE:若其值为 1 则会通知对方,从对方到这边的网络有阻塞。在收到数据包的 IP 首部中 ECN 为 1 时将 TCP 首部中的 ECE 设为 1.;

URG:该位设为 1,表示包中有需要紧急处理的数据,对于需要紧急处理的数据,与后面的紧急指针有关;

ACK:该位设为 1,确认应答的字段有效,TCP规定除了最初建立连接时的 SYN 包之外该位必须设为 1;

PSH:该位设为 1,表示需要将收到的数据立刻传给上层应用协议,若设为 0,则先将数据进行缓存;

RST:该位设为 1,表示 TCP 连接出现异常必须强制断开连接;

SYN:用于建立连接,该位设为 1,表示希望建立连接,并在其序列号的字段进行序列号初值设定;

FIN:该位设为 1,表示今后不再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位置为 1 的 TCP 段。每个主机又对对方的 FIN 包进行确认应答之后可以断开连接。不过,主机收到 FIN 设置为 1 的 TCP 段之后不必马上回复一个 FIN 包,而是可以等到缓冲区中的所有数据都因为已成功发送而被自动删除之后再发 FIN 包;

·Window Size:TCP的流量控制由每个端点使用窗ロ大小字段来通告一个窗口大小来完成。这个窗口大小是字节数,从ACK号指定的,也是接收方想要接收的那个字节开始。这是一个16位的字段,限制了窗口大小到65535字节,从而限制了TCP的吞吐量性能。在第15章我们将看到的窗口和改进性能。窗口缩放( Window Scale)选项可允许对这个值进行缩放,给高速和大延迟网络提供了更大

·Checksum:TCP 的检验和与 UDP 检验和一样,也是采用伪首部,但是 TCP 的检验和无法关闭。TCP 伪首部的信息和 UDP 一样,包括:源 IP 地址、目的 IP 地址、填充、协议号以及 TCP 包长度;

·Urgent Pointer:紧急指针(Urgent Pointer)字段只有在URG位字段被设置时才有效。这个“指针”是个必须要加到报文段的序列号字段上的正偏移,以产生紧急数据的最后一个字节的序列号。TCP的紧急机制是一种让发送方给另一端提供特殊标志数据的方法。

·Option and Pad:选项和填充,n*4字节,常见的可选字段是最长报文大小 MSS(Maximum Segment Size) 。每个连接方通常都在通信的第一个报文段(为建立连接而设置 SYN 标志的那个段)中指明这个选项,它指明本端所能接收的最大长度的报文段。选项长度不一定是 32 位字的整数倍,所以要加填充位,使得报头长度成为整字数

·Data:数据,不定长度,为上层协议封装好的数据

●重发超时如何确定
重发超时是指在重发数据之前,等待确认应答到来的那个特定时间间隔。如果超过了这个时间仍未收到确认应答,发送端将进行数据重发。重发超时一般都是0.5秒的整数倍,由于最初的数据包还不知道往返时间,所以其重发超时一般设置为6秒左右。数据被重发之后若还是收不到确认应答,则进行再次发送。此时,等待确认应答的时间将会以2倍、4倍的指数函数延长。此外,数据也不会被无限、反复地重发。达到一定重发次数以后,如果仍没有任何确认应答返回,就会判断为网络或对端主机发生了异常,强制关闭连接。并且通知应用通信异常强行终止。

●窗口控制
TCP 传输数据是以 1 个段为单位,每发送一个段进行一次确认应答的处理,这样使通信时包的往返时间很长导致降低通信性能。为了解决这个问题,TCP 引入了窗口控制,确认应答不再是以每个分段,而是以更大的单位进行确认,这样缩短转发时间,也就是说,发送端主机在发送了一个段之后不必要一直等待确认应答,而是继续发送数据段。窗口大小是指无需等待确认应答而可以发送数据的最大值。
TCP IP协议原理精讲_第11张图片
采用窗口控制机制必须实现缓冲区,在图 4 中,窗口内的数据即便是没有收到确认应答也可以发送出去。此外从该窗口中能看到的数据是因其某种数据已在传输中丢失,所以发送端才能接收到确认应答,这种情况下需要进行重发。为此,发送端主机在等到确认应答返回之前,必须在缓冲区中保留这部分的数据。在滑动窗口以外的部分包括尚未发送的数据和已经确认对端已经收到的数据。当数据发出后若如期收到确认应答就可以不用进行重发,此时数据可以从缓冲区中删除。收到确认应答后,将窗口滑到确认应答中的序列号的位置,这样可以顺序地将多个段同时发送提供通信性能。这种机制也称为滑动窗口机制。

●流量控制
流量控制可以让发送端根据接收端的实际接受能力控制发送的数据量。它的具体操作是,接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不会超过该大小的数据,该限制大小即为窗口大小,即窗口大小由接收端主机决定。TCP 首部中,专门有一个字段来通知窗口大小,接收主机将自己可以接收的缓冲区大小放在该字段中通知发送端。当接收端的缓冲区面临数据溢出时,窗口大小的值也是随之改变,设置为一个更小的值通知发送端,从而控制数据的发送量,这样达到流量的控制。

●拥塞控制
一般来说,计算机网络都处在一个共享的环境。因此,有可能因为其他主机之间的通信使得网络拥堵。在网络出现拥堵时,如果突然发送一个较大量的数据,极有可能会导致整个网络瘫痪。TCP为了防止网络拥塞,TCP 采用了一种慢启动算法,对发送数据量进行控制。为了调节发送端的数据发送量,引入了拥塞窗口,在慢启动时,将这个拥塞窗口设为 1 个数据段发送数据,之后每收到一次确认应答,拥塞窗口的值就加 1。在发送数据包时,将拥塞窗口的大小与接收端主机通知的窗口大小进行比较,然后选择较小的值来控制数据量的发送。

●TCP连接的三次握手
所谓的三次握手即TCP连接的建立。这个连接必须是一方主动打开,另一方被动打开的。以下为客户端主动发起连接的图解:
TCP IP协议原理精讲_第12张图片
握手之前主动打开连接的客户端结束CLOSED阶段,被动打开的服务器端也结束CLOSED阶段,并进入LISTEN阶段。随后开始“三次握手”:

(1)首先客户端向服务器端发送一段TCP报文,其中:
•标记位为SYN,表示“请求建立新连接”;
•序号为Seq=X(X一般为1);
•随后客户端进入SYN-SENT阶段。

(2)服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:
•标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);
•序号为Seq=y;
•确认号为Ack=x+1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值;随后服务器端进入SYN-RCVD阶段。

(3)客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:
•标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);
•序号为Seq=x+1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值;
•确认号为Ack=y+1,表示收到服务器端序号Seq,并将其值加1作为自己的确认号Ack的值;
•随后客户端进入ESTABLISHED阶段。
服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。

在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。

此后客户端和服务器端进行正常的数据传输。这就是“三次握手”的过程。

• “三次握手”的通俗理解
TCP IP协议原理精讲_第13张图片
举个栗子:把客户端比作男孩,服务器比作女孩。用他们的交往来说明“三次握手”过程:
(1)男孩喜欢女孩,于是写了一封信告诉女孩:我爱你,请和我交往吧!;写完信之后,男孩焦急地等待,因为不知道信能否顺利传达给女孩。
(2)女孩收到男孩的情书后,心花怒放,原来我们是两情相悦呀!于是给男孩写了一封回信:我收到你的情书了,也明白了你的心意,其实,我也喜欢你!我愿意和你交往!;
写完信之后,女孩也焦急地等待,因为不知道回信能否能顺利传达给男孩。
(3)男孩收到回信之后很开心,因为发出的情书女孩收到了,并且从回信中知道了女孩喜欢自己,并且愿意和自己交往。然后男孩又写了一封信告诉女孩:你的心意和信我都收到了,谢谢你,还有我爱你!
女孩收到男孩的回信之后,也很开心,因为发出的情书男孩收到了。由此男孩女孩双方都知道了彼此的心意,之后就快乐地交流起来了~~
这就是通俗版的“三次握手”,期间一共往来了三封信也就是“三次握手”,以此确认两个方向上的数据传输通道是否正常。

•为什么要进行第三次握手?
为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
由于网络传输是有延时的(要通过网络光纤和各种中间代理服务器),在传输的过程中,比如客户端发起了SYN=1创建连接的请求(第一次握手)。
如果服务器端就直接创建了这个连接并返回包含SYN、ACK和Seq等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直没有接收到服务器返回的数据包。
客户端可能设置了一个超时时间,时间到了就关闭了连接创建的请求。再重新发出创建连接的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器端客户端收的到服务器端传输的数据的话,
服务器端是不知道客户端有没有接收到服务器端返回的信息的。

这个过程可理解为:
TCP IP协议原理精讲_第14张图片
这样没有给服务器端一个创建还是关闭连接端口的请求,服务器端的端口就一直开着,等到客户端因超时重新发出请求时,服务器就会重新开启一个端口连接。那么服务器端上没有接收到请求数据的上一个端口就一直开着,长此以往,这样的端口多了,就会造成服务器端开销的严重浪费。

还有一种情况是已经失效的客户端发出的请求信息,由于某种原因传输到了服务器端,服务器端以为是客户端发出的有效请求,接收后产生错误。

所以我们需要“第三次握手”来确认这个过程,让客户端和服务器端能够及时地察觉到因为网络等一些问题导致的连接创建失败,这样服务器端的端口就可以关闭了不用一直等待。

也可以这样理解:“第三次握手”是客户端向服务器端发送数据,这个数据就是要告诉服务器,客户端有没有收到服务器“第二次握手”时传过去的数据。若发送的这个数据是“收到了”的信息,接收后服务器就正常建立TCP连接,否则建立TCP连接失败,服务器关闭连接端口。由此减少服务器开销和接收到失效请求发生的错误。

5.抓包验证

下面是用抓包工具抓到的一些数据包,可用来分析TCP的三次握手:
在这里插入图片描述

TCP IP协议原理精讲_第15张图片

TCP IP协议原理精讲_第16张图片

TCP IP协议原理精讲_第17张图片
图中显示的就是完整的TCP连接的”三次握手”过程。在59741 -> 6000中,52528是本地(客户端)端口,6000是服务器的端口。6000端口和52528端口之间的三次来回就是"三次握手"过程。

①注意到”第一次握手”客户端发送的TCP报文中以[SYN]作为标志位,并且客户端序号Seq=0;
②接下来”第二次握手”服务器返回的TCP报文中以[SYN,ACK]作为标志位;并且服务器端序号Seq=0;确认号Ack=1(“第一次握手”中客户端序号Seq的值+1);
③最后”第三次握手”客户端再向服务器端发送的TCP报文中以[ACK]作为标志位;

其中客户端序号Seq=1(“第二次握手”中服务器端确认号Ack的值);确认号Ack=1(“第二次握手”中服务器端序号Seq的值+1)。

这就完成了”三次握手”的过程,符合前面分析的结果

●TCP断开的四次挥手
对于"三次握手"我们耳熟能详,因为其相对的简单。但是,我们却不常听见“四次挥手”,就算听过也未必能详细地说明白它的具体过程。下面就为大家详尽,直观,完整地介绍“四次挥手”的过程。
所谓的四次挥手即TCP连接的释放(解除)。连接的释放必须是一方主动释放,另一方被动释放。以下为客户端主动发起释放连接的图解:
TCP IP协议原理精讲_第18张图片
挥手之前主动释放连接的客户端结束ESTABLISHED阶段。随后开始“四次挥手”:

(1)首先客户端想要释放连接,向服务器端发送一段TCP报文,其中:
标记位为FIN,表示“请求释放连接“;
序号为Seq=U;
随后客户端进入FIN-WAIT-1阶段,即半关闭阶段。并且停止在客户端到服务器端方向上发送数据,但是客户端仍然能接收从服务器端传输过来的数据。
注意:这里不发送的是正常连接时传输的数据(非确认报文),而不是一切数据,所以客户端仍然能发送ACK确认报文。

(2)服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态)并返回一段TCP报文,其中:
标记位为ACK,表示“接收到客户端发送的释放连接的请求”;
序号为Seq=V;
确认号为Ack=U+1,表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值;
随后服务器端开始准备释放服务器端到客户端方向上的连接。
客户端收到从服务器端发出的TCP报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段
前"两次挥手"既让服务器端知道了客户端想要释放连接,也让客户端知道了服务器端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务器端方向上的连接了

(3)服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文,其中:
标记位为FIN,ACK,表示“已经准备好释放连接了”。注意:这里的ACK并不是确认收到服务器端报文的确认报文。
序号为Seq=W;
确认号为Ack=U+1;表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值。
随后服务器端结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。

(4)客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文,其中:
标记位为ACK,表示“接收到服务器准备好释放连接的信号”。
序号为Seq=U+1;表示是在收到了服务器端报文的基础上,将其确认号Ack值作为本段报文序号的值。
确认号为Ack=W+1;表示是在收到了服务器端报文的基础上,将其序号Seq值作为本段报文确认号的值。
随后客户端开始在TIME-WAIT阶段等待2MSL
什么是2MSL
MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为tcp报文(segment)是ip数据报(datagram)的数据部分,具体称谓请参见《数据在网络各层中的称呼》一文,而ip头中有一个TTL域,TTL是time to live的缩写,中文可以译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个ip数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。

TCP四次挥手的等待时间为什么是2MSL而不是1MSL
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中可以通过设置SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。
这就是为什么要通过setsockopt()函数设置“允许绑定地址快速复用”的原因;如果不设置的话,通常情况下服务器端关闭后,需要等待2分钟左右才能重新启动服务器;

为什么要客户端要等待2MSL呢?见后文。

服务器端收到从客户端发出的TCP报文之后结束LAST-ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。

客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段,由此完成“四次挥手”。

后“两次挥手”既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成“四次挥手”。

与“三次挥手”一样,在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性,一旦出现某一方发出的TCP报文丢失,便无法继续"挥手",以此确保了"四次挥手"的顺利完成。
•四次挥手”的通俗理解
TCP IP协议原理精讲_第19张图片
举个栗子:把客户端比作男孩,服务器比作女孩。通过他们的分手来说明“四次挥手”过程。
•“第一次挥手”:日久见人心,男孩发现女孩变成了自己讨厌的样子,忍无可忍,于是决定分手,随即写了一封信告诉女孩。

•“第二次挥手”:女孩收到信之后,知道了男孩要和自己分手,怒火中烧,心中暗骂:你算什么东西,当初你可不是这个样子的!于是立马给男孩写了一封回信:分手就分手,给我点时间,我要把你的东西整理好,全部还给你!男孩收到女孩的第一封信之后,明白了女孩知道自己要和她分手。随后等待女孩把自己的东西收拾好。
•“第三次挥手”:过了几天,女孩把男孩送的东西都整理好了,于是再次写信给男孩:你的东西我整理好了,快把它们拿走,从此你我恩断义绝!
•“第四次挥手”:男孩收到女孩第二封信之后,知道了女孩收拾好东西了,可以正式分手了,于是再次写信告诉女孩:我知道了,这就去拿回来!

这里双方都有各自的坚持。

•女孩自发出第二封信开始,限定一天内收不到男孩回信,就会再发一封信催促男孩来取东西!
•男孩自发出第二封信开始,限定两天内没有再次收到女孩的信就认为,女孩收到了自己的第二封信;若两天内再次收到女孩的来信,就认为自己的第二封信女孩没收到,需要再写一封信,再等两天……

倘若双方信都能正常收到,最少只用四封信就能彻底分手!这就是“四次挥手”。

•为什么“握手”是三次,“挥手”却要四次?
TCP建立连接时之所以只需要"三次握手",是因为在第二次"握手"过程中,服务器端发送给客户端的TCP报文是以SYN与ACK作为标志位的。SYN是请求连接标志,表示服务器端同意建立连接;ACK是确认报文,表示告诉客户端,服务器端收到了它的请求报文。

即SYN建立连接报文与ACK确认接收报文是在同一次"握手"当中传输的,所以"三次握手"不多也不少,正好让双方明确彼此信息互通。

TCP释放连接时之所以需要“四次挥手”,是因为FIN释放连接报文与ACK确认接收报文是分别由第二次和第三次"握手"传输的。为何建立连接时一起传输,释放连接时却要分开传输?

•建立连接时,被动方服务器端结束CLOSED阶段进入“握手”阶段并不需要任何准备,可以直接返回SYN和ACK报文,开始建立连接。
•释放连接时,被动方服务器,突然收到主动方客户端释放连接的请求时并不能立即释放连接,因为还有必要的数据需要处理,所以服务器先返回ACK确认收到报文,经过CLOSE-WAIT阶段准备好释放连接之后,才能返回FIN释放连接报文。

所以是“三次握手”,“四次挥手”。

•为什么客户端在TIME-WAIT阶段要等2MSL?

为的是确认服务器端是否收到客户端发出的ACK确认报文

当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。

服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;

•如果客户端在2MSL内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;

•否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。

所以,客户端要经历时长为2SML的TIME-WAIT阶段;这也是为什么客户端比服务器端晚进入CLOSED阶段的原因

•抓包验证
图中显示的就是完整的TCP连接释放的”四次挥手”过程。在 6000 -> 59741 中,假设6000是本地(客户端)端口,59741是服务器端口。6000端口与59741之间的四次来回就是"四次挥手"过程。

•”第一次挥手”客户端发送的FIN请求释放连接报文以[FIN,ACK]作为标志位,其中报文序号Seq=2445;确认号Ack=558;注意:这里与“第三次握手”的ACK并不是表示确认的ACK报文。
•”第二次挥手”服务器端返回的ACK确认报文以[ACK]作为标志位;其中报文序号Seq=558;确认号Ack=2246;
•”第三次挥手”服务器端继续返回的FIN同意释放连接报文以[FIN,ACK]作为标志位;其中报文序号Seq=558;确认号Ack=2246;
•”第四次挥手”客户端发出的ACK确认接收报文以[ACK]作为标志位;其中报文序号Seq=2446;确认号Ack=559。

后一次“挥手”传输报文中的序号Seq值等于前一次"握手"传输报文中的确认号Ack值;后一次“挥手”传输报文中的确认号Ack值等于前一次"握手"传输报文中的序号Seq值;

故这是连续的“四次挥手”过程,与前面的分析相符。

你可能感兴趣的:(C语言网络编程)