TCP协议详解

TCP协议详解,第1张

TCP协议详解

简介:大家好,我是枫哥,一线互联网的IT民工、资深面试官、Java跳蚤网课堂创始人。拥有多年一线研发经验,曾就职过科大讯飞、美团网、平安等公司。在上海有自己小伙伴组建的副业团队,目前业余时间专注Java技术分享,春招/秋招/社招/跳槽,一对一学习辅助,项目接活开发。

扫码左侧二维码,加入群聊,一起学习,一起进步!

 欢迎关注  收藏 留言 

:文末送福利

TCP协议属于传输层协议。从通信和信息处理的角度来看,属于通信部分的最高层,只有位于网络边缘的主机协议栈才有传输层协议;同时也是用户功能的最低层,一些重要的socket选项与TCP协议有关。

TCP服务的特点。


传输协议主要有两种:TCP协议和UDP协议。与UDP协议相比,TCP协议具有面向连接、字节流和可靠传输的特点。


使用TCP协议通信的双方必须在开始数据读写之前建立连接。双方必须为连接分配必要的核心资源,以管理连接状态和连接上的数据传输。TCP连接是全双工的,即双方的数据读写可以通过一个连接进行。数据交换完成后,通信双方必须断开连接,以释放系统资源。
TCP协议的这种连接是一对一的,所以TCP服务不能用于基于广播和多播(目标是多个主机地址)的应用。无连接协议UDP非常适合广播和多播。


TCP提供的字节流服务没有边界(长度)限制,不断从通信的一端流入另一端。UDP提供的数据报告服务每个数据报告都有一个长度,接收器必须以此长度为最小单位一次性读取所有内容,否则数据将被切断。


TCP传输是可靠的。首先,TCP协议采用发送响应机制,即发送端发送的每个TCP报文段都必须得到接收方的响应,才能认为TCP报文段传输成功。其次,TCP协议采用超时重传机制,发送方在发送TCP报文段后启动定时器。如果在固定时间内没有收到响应,将重新发送报文段。最后,由于TCP报文段最终是以IP数据报告发送的,IP数据报告到达接收端可能是乱七八糟、重复的,TCP协议会对接收到的TCP报文段进行重新安排、整理,然后交付给应用层。UDP协议和IP协议一样,提供不可靠的服务。他们都需要上级协议来处理数据确认和超时重传。


TCP头部结构


TCP头部结构如下图所示,其中许多字段为管理TCP连接和控制数据流提供了足够的信息。

 

16位端口号:告知主机报文段来自哪里(源端口),传输给哪个上层协议或应用程序(目的端口)。在TCP通信中,客户端通常使用系统自动选择的临时端口号,而服务器使用知名服务端口号。


32位序号:TCP通信(从TCP连接方向的字节流的每个字节的编号(从TCP连接建立到断开)。假设主机A和主机B进行TCP通信,在A发送给B的第一个TCP报文段中,序号值被系统初始化为随机值ISN(InitialSequencenumber,初始序号值)。然后在传输方向(从A到B),系统会将后续TCP报文段的序号值设置为ISN,并将报文段携带的数据的第一个字节偏移到整个字节流中。比如一个TCP报文段传输的数据是字节流中的第1025~2048字节,那么报文段的序号值就是ISN+1025。另一个传输方向(从B到A)的TCP报文段序号值也有同样的含义。


32位确认号:用于对方发送的TCP报文段的响应。值为收到的TCP报文段序号值加1。假设主机A和主机B通信TCP,A发送的TCP报文段不仅携带自己的序号,还包含B发送的TCP报文段的确认号。相反,B发送的TCP报文段也携带自己的序号和对A发送的报文段的确认号。


四个头部长度:标出TCP头部有多少个32bit字(4字节)。TCP头部最长为60字节,因为四个头部最多能表示15个字节。


六位标志位包括以下几项:

 URG标志表示紧急指针是否有效。ACK标志,表示确认号是否有效。带ACK标志的TCP报文段为确认报文段。PSH标志提示接收端应用程序应立即从TCP接收缓冲区读取数据,为接收后续数据腾出空间(如果应用程序不读取接收数据,它们将永远停留在TCP接收缓冲区)。RST标志,表示要求对方重新建立连接。带RST标志的TCP报文段为复位报文段。SYN标志,表示请求建立连接。带SYN标志的TCP报文段为同步报文段。FIN标志,表示通知对方本端关闭连接。带FIN标志的TCP报文段为结束报文段。16个窗口大小:是控制TCP流量的手段。这里的窗口是指接收通知的窗口。它告诉对方TCP接收缓冲区能容纳多少字节数据,让对方控制发送数据的速度。16个校准和:由发送端填充,接收端对TCP报文段执行CRC算法,检查TCP报文段在传输过程中是否损坏。请注意,此校准不仅包括TCP头部,还包括数据部分。这也是TCP可靠传输的重要保证。16位紧急指针:是正偏移。它与序号字段的值相加,表示下一个字节最后一个紧急数据的序号。所以确切的说,这个字段是紧急指针与当前序号相比的偏移,不妨称之为紧急偏移。TCP的紧急指针是发送端向接收端发送紧急数据的方法。  建立和关闭TCP连接(三次握手和四次挥手)

 

我们用tcpdump抓取上述两台测试机交换的TCP报文段,观察TCP连接和关闭的过程。
首先,从ernest-laptop上执行teinet命令登录Kongming20的80端口,然后抓取客户端和服务器在此过程中交换的TCP报文段。具体 *** 作流程如下:

 

在执行telnet命令并在两台通信主机之间建立TCP连接时(telnet输出Conectedto192.168.1.109),输入Ctrl+]调出telnet程序的命令提示符,然后在telnet命令提示符后输入quit退出telnet客户端程序,从而结束TCP连接。在整个过程中,tcpdump输出的内容如代码列表3-2所示。

由于整个过程中没有应用层数据交换,TCP报文段数据部分的长度总是为0。为了更清楚地表示建立和关闭TCP连接的整个过程,我们将TCPdump输出的内容绘制成图3-6所示的时序图。

 

第一个TCP报文段包含SYN标志,所以是一个同步报文段,即enmest-laptop(客户端)向Kongming20(服务器)发起连接请求。同时,同步报文段包含一个序号,ISN值为535734930。第二个TCP报文段也是同步报文段,表示Kongming20同意与ernest-laptop建立连接。同时,它发送自己的ISN值为2159701207序号,并确认第一个同步报文段。确认值为535734931,即第一个同步报文段序号值加1。前面说过,序号值是用来识别TCP数据流中的每个字节的。但是同步报文段很特殊,即使没有携带任何应用程序数据,也要占用序号值。第三个TCP报文段是erest-lap对第二个同步报文段的确认。到目前为止,TCP连接已经建立。建立了这三个步骤。


从第三个TCP报文段开始,tcpdump输出的序号值和确认值都偏离了初始ISN值。当然,我们可以打开tcpdump的-S选项来选择打印序号的绝对值。


后四个TCP报文段是关闭连接的过程。第四个TCP报文段包含FIN标志,所以是结束报文段,即emest-laptop要求关闭连接。结束报文段和同步报文段一样,也要占用序号值。Kongming20使用TCP报文段5来确认结束报文段。然后Kongming20发送自己的结束报文段6,ernest-laptop用TCP报文段7确认。事实上,仅用于确认目的的确认报文段5是可以省略的,因为结束报文段6也携带了确认信息。确认报文段5是否出现在连接断开的过程中,取决于TCP延迟确认的特点。延迟确认将在后面讨论。


如果客户端访问远离它的服务器,或者由于网络繁忙,服务器没有回应客户端发送的同步报文段,客户端程序必须先重新连接(可能执行多次)。如果重新连接仍然无效,通知应用程序连接加班。

TCP状态转移 

TCP连接的任何一端在任何时候都处于某种状态,目前的状态可以通过netstat命令来看。图3-8是完整的状态转移图,描述了所有TCP状态和可能的状态转换。

 

图3-8中的粗虚线表示服务器端连接的典型状态转移;粗实线表示客户端连接的典型状态转移。CLOSED是假设的起点,不是实际状态。

服务器端的状态转移过程

 服务器通过listen系统调用进入LISTEN状态,被动等待客户端连接,因此执行所谓的被动开启。一旦服务器监控某个连接请求(收到同步报文段),将连接放入核心等待队列,并将带SYN标志的确认报文段发送给客户端。此时,连接处于SYN_RCVD状态。如果服务器成功接收到客户端发送的确认报文段,连接将转移到ESTABLISHED状态。ESTABLISHED状态是双向数据传输的状态。


当客户端主动关闭连接时(通过close或shutdown系统调用向服务器发送结束报文段),服务器返回确认报文段,使连接进入CLOSE_WAIT状态。这种状态的含义很明确:等待服务器应用程序关闭连接。通常,服务器检测到客户端关闭连接后,会立即向客户端发送结束报文段来关闭连接。这将使连接转移到LAST_ACK状态,等待客户端最后一次确认结束报文段。一旦确认完成,连接将完全关闭。

客户端的的状态转移过程

客户端通过conect系统调用主动与服务器建立连接。connect系统调用首先向服务器发送同步报告段,使连接转移到SYN_SENT状态。此后,conect系统调用可能因以下两个原因未能返回:

如果conect连接的目标端口不存在(没有任何过程监控),或者端口仍然被TIME_WAIT连接占用,服务器将向客户端发送复位报文段,conect调用失败。如果目标端口存在,但conect在超时时间内没有收到服务器的确认报文段,conect调用失败。 

 如果connect调用失败,连接将立即返回到最初的CLOSED状态。如果客户端成功收到服务器的同步报文段并确认,conect调用将成功返回,连接将转移到ESTABLISHED状态。当客户端主动关闭时,它会向服务器发送一个结束报文段,同时连接到FIN_WAIT_1状态。如果客户端收到专门用于确认目的的确认报文段(如图3-6中的TCP报文段5)的服务器,则连接将转移到FIN_WAIT_2状态。当客户端处于FIN_WAIT_2状态时,服务器处于CLOSE_WAIT状态,这种状态可能此时,如果服务器也关闭连接(发送结束报文段),客户端将确认并进入TIME_WAIT状态。


TCP连接是全双工的,所以允许两个方向的数据传输独立关闭。换句话说,通信的一端可以向对方发送报文段,告诉对方数据的发送已经完成,但允许对方继续接收数据,直到对方发送结束的报文段关闭连接。TCP连接的这种状态称为半关闭状态,如下图3-7所示。

 

 图3-8还给出了客户端直接从FIN_WAIT_1状态进入TIME_WAIT状态的线路(不经过FIN_WAIT_2状态),前提是处于FIN_WAIT_1状态的服务器直接收到带确认信息的结束报文段(而不是先收到确认报文段,再收到结束报文段)。这种情况对应于图3-6中的服务器不发送TCP报文段5。


正如我前面所说,处于FIN_WAIT_2状态的客户端需要等待服务器发送报文段,才能转移到TIME_WAIT状态,否则会一直停留在这种状态。如果不是为了在半关闭状态下继续接收数据,连接长时间停留在FIN_WAIT_2状态是没有好处的。连接停留在FIN_WAIT_2状态可能发生在客户端半关闭后,服务器关闭连接前强行退出。此时,客户端连接由内核接管,可称为孤儿连接(类似于孤儿过程)。为了防止孤儿连接长时间留在内核中,Linux定义了两个内核变量:/proc/sys/net/ipv4/tcp_maphans和/proc/sys/ipv4/tcp_fimeout。前者指定孤儿连接的数量,后者指定孤儿连接在内核中的生存时间。

 TIME_WAIT 状态

从图3-9来看,客户端连接并没有直接进入CLOSED状态,而是转移到TIME_WAIT状态。在这种状态下,客户端连接需要等待一段时间才能完全关闭。MSL是TCP报文段在网络上的最大生存时间,标准文档RFC1122的建议值为2分钟。
WAIT状态存在的原因有两个:

TCP连接可靠终止。确保迟来的TCP报文段有足够的时间被识别和丢弃。

第一个原因很好理解。假设图3-9中用于确认服务器结束报文段6的TCP报文段7丢失,那么服务器将重新发布结束报文段。因此,客户端需要停留在一定状态处理重复收到的结束报文段(即向服务器发送确认报文段)。否则客户端会通过复位报文段来回应服务器,服务器认为这是错误的,因为它期望的是TCP报文段7这样的确认报文段。


在Linux系统中,一个TCP端口不能同时打开多次(两次以上)。当一个TCP连接处于TIME_WAIT状态时,我们将无法立即使用连接占用的端口来建立一个新的连接。另一方面,如果没有TIME_WAIT状态,应用程序可以立即建立类似于刚刚关闭连接的连接(这里的类似意味着它们有相同的IP地址和端口号)。这种新的、类似于原始的连接被称为原始连接的化身,新的化身可能会接收到原始连接的TCP报文段(迟到报文段),这显然不应该发生。这是TIME_WAIT状态存在的第二个原因。


此外,由于TCP报文段最大的生存时间是MSL,坚持2MSL时间的TIME_WAIT状态可以保证网络上两个传输方向尚未收到,迟到的TCP报文段已经消失(被中转路由器丢弃)。因此,一个连接的新化身可以在2MSL时间后安全建立,永远不会收到原始连接的应用数据,这也是TIME_WAIT状态持续2MSL时间的原因。


有时候我们希望避免TIME_WAIT状态,因为当程序退出时,我们希望立即重启。但由于TIME_WAIT状态的连接仍然占用端口,程序将无法启动(直到2MSL超时结束)。


对于客户端程序,我们通常不用担心上面描述的重启。由于客户端通常使用系统自动分配的临时端口号来建立连接,由于随机性,临时端口号通常与程序上次使用的端口号不同(仍处于TIME_WAIT状态的端口号),因此客户端程序通常可以立即重启。除非我们强迫客户端使用固定端口。
但是,如果服务器在主动关闭连接后异常终止,连接的TIME_WAIT状态会导致其无法立即重启,因为它总是使用相同的知名服务端口号。但是,我们可以通过socket选项SO_REUSEADDR迫使过程立即使用处于TIME_WAIT状态的连接占用端口。

超时重传

在异常网络条件下(开始超时或丢包),为了保证TCP承诺的可靠服务,TCP服务必须能够在超时时间内重新传输未确认的TCP报文段。因此,TCP模块为每个TCP报文段维护一个重传定时器,该定时器在TCP报文段首次发送时启动。如果在超时时间内没有收到接收方的回复,TCP模块将重新传输TCP报文段并重置定时器。TCP的重传策略是如何选择下次重传的超时时间,实施多少次重传。


虽然加班会导致TCP报文段重传,但TCP报文段重传可以发生在加班前,即快速重传。

拥塞控制

TCP模块的另一个重要任务是提高网络利用率,降低丢包率,保证网络资源对每个数据流的公平性。这就是所谓的拥堵控制。


TCP拥塞控制的标准文件是RFC5681,详细介绍了拥塞控制的四个部分:慢启动、避免拥塞、快速重传和快速恢复。


拥塞控制的最终控制变量是发送端连续写入网络(收到第一个数据确认前)的数据量,我们称之为SWND(SendWindow,发送窗口)。然而,发送端最终通过TCP报文段发送数据,因此SWND限制了发送端可以连续发送的TCP报文段数量。这些TCP报文段的最大长度(仅指数据部分)称为SMSS(SenderMaximumSegmentSize,发送者最大段大小),一般等于MSS。


发送端需要合理选择SWND的大小。如果SWND太小,会造成明显的网络延迟;相反,如果SWND太大,很容易导致网络拥塞。如前所述,接收器可以通过其接收通知窗口(RWND)来控制发送器的SWND。但这显然是不够的,所以发送器引入了一个状态变量,叫做拥塞窗口(CongestionWindow,CWND)。实际SWND值是RWND和CWND中的较小者。图3-11显示了拥塞控制的输出和输出(可见是闭环反馈控制)。

 感谢大家,坚持看完,既然选择了这条路,那就一起加油,一起学习!如果需要学习资源,实战面试资料,项目资源。关注公众号:IT枫斗者,根据关键字领取对应的资料福利!咨询解决问题,公众号私聊枫哥,备注来意。

回复:java全套学习资源

回复:面试资料

回复:枫哥简历

回复:程序员表白神器               

(从此告别程序员单身狗!)

回复:程序员兼职网站

回复:枫哥666                             

( 获取66套项目实战资料,大厂面试视频)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5721892.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存