TCP三次握手四次断开

linux 专栏收录该内容
8 篇文章 0 订阅

转载地址:www.51niux.com

IP协议是网络层的主要协议,为上层传输层提供无连接、无状态、不可靠的服务。优点是简单高效。无状态是指各个IP报文是独立传送的,不同步传输状态的信息,所以容易发生重复和乱序的情况。不可靠是指IP协议不能保证数据报一定能被送达,可靠性主要是通过传输层的TCP协议来保证的。
TCP协议是面向连接的协议,是工作在传输层的协议。TCP协议通过三次握手、四次挥手、流量控制、拥塞控制、超时重传、确认报文等机制来保证可靠性。

IP报文首部

在这里插入图片描述
第一层:前四个字节是版本号(IPv4或IPv6),下面后四个字节是包头的首部长度,首部长度(HLEN)这个四位字段定义了数据报首部的总长度,首部长度是可变的(在20-60字节之间)在没有选项时,首部长度是20字节,且这个字段的值是5(5*4=20)。后面8位是服务类型用来获得更好的服务,在旧标准中叫做服务类型,但实际上一直未被使用过.1998 年这个字段改名为区分服务.只有在使用区分服务(DiffServ)时,这个字段才起作用.一般的情况下都不使用这个字段,后面的16是报文的总长度,总长度-首部长度,就是数据长度

第二层:段标识符(IP报文太长需要分片,就需要段标识来标示),标志占三位占3位,目前只有前两位有意义,中间位是DF标示,不能分片只有当 DF=0 时才允许分片(如果不允许分片发送不出去就返回错误信息了),最后一位是MF标示更多的分片,MF=1 表示后面“还有分片”。MF=0 表示最后一个分片。最后的是用来定义片偏移量,占12位,指较长的分组在分片后某片在原分组中的相对位置.片偏移以 8 个字节为偏移单位,越小越在前面。

第三层:生存时间,每经过一个网关设备TTL就-1,减到0还没有到设备就丢弃,TTL 字段是由发送端初始设置一个8 bit字段.推荐的初始值由分配数字 RFC 指定,当前值为 64.发送 ICMP 回显应答时经常把 TTL 设为最大值 255。后8个字节标示协议类型,指出此数据报携带的数据使用何种协议以便目的主机的IP层将数据部分上交给哪个处理过程, 1表示为 ICMP 协议, 2表示为 IGMP 协议, 6表示为 TCP 协议, 17表示为 UDP 协议。最后是首部校验和,存放首部的校验码。

第四层:源IP

第五层:目标IP

第六层:标记是什么协议什么进程进行的通信,端口号的范围是0-65535,linux主机一般0-1023端口是系统占用的,大于5000的端口才允许客户端随意使用呢,linux没打开一个端口就是打开了一个套接字文件。

第七层:数据。这是在数据报中要传输的数据。它是一个完整的较高层报文或报文的一个分片。

TCP报文首部

在这里插入图片描述
第一层:发送方的端口号,接收方的端口号

第二层:32位序列号:也就是我们tcp三次握手中的seq,表示的是我们tcp数据段发送的第一个字节的序号,范围[0,2^32 - 1]

第三层:32位确认序列号:也就是ack,序列号+1就是确认号。

第四层:首部长度,后面是6个保留位置,URG标示紧急指针(URG为1表示有效,0表示无效)它告诉系统中有紧急数据,应当尽快传送,这时不会按照原来的排队序列来传送.而会将紧急数据插入到本报文段数据的最前面.ACK用于说明确认号是否有效,当ACK=1时,我们的确认序列号ack才有效,当ACK=0时,确认序号ack无效,TCP规定:所有建立连接的ACK必须全部置为1.PSH推送,一旦发生推送报文立即送往内核,RST表示重置,当RST=1时,表明TCP连接出现严重错误,此时必须释放连接,之后重新连接,又叫重置位。SYN发送同步请求。FYN断开连接。最后是窗口大小。

第五层:TCP的校验码,可选长度的可选数据

第六层:其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,那么选项部分最长为:(2^4-1)*4-20=40字节。选项部分的应用:MSS最大报文段长度(Maxium Segment Size),窗口扩大选项(Windows Scaling),SACK选择确认项(Selective Acknowledgements),时间戳(Timestamps),NOP(NO-Operation)

第七层:数据。

字段含义
URG紧急指针是否有效。为1,表示某一位需要被优先处理
ACK确认号是否有效,一般置为1。
PSH提示接收端应用程序立即从TCP缓冲区把数据读走。
RST对方要求重新建立连接,复位。
SYN请求建立连接,并在其序列号的字段进行序列号的初始值设定。建立连接,设置为1
FIN希望断开连接。

TCP三次握手

在这里插入图片描述
第一次握手:
客户端发送一个TCP的SYN标志位置1的包,ACK=0,TCP规定SYN=1时不能携带数据,当SYN=1而ACK=0时,表明这是一个连接请求报文但要消耗一个序号,因此声明自己的序号是seq=i。Seq:序号,4字节,范围为032—132,共4284967296,达到时重新开始计算。Clinet进入SYN_SENT状态,等待Server确认。所以最终客户端发送的报文中包含SYN=1,ACK=0,seq=i(i为一个随机数)。

第二次握手:
服务器发回确认包(ACK)应答。即SYN=1 ACK=1,因为建立连接,则应在响应报文中使SYN=1和ACK=1。seq=j(产生的随机包序号),ack=i+1(确认客户端序号有效)。所以发送给客户端的包里面包含:SYN=1,ACK=1,seq=j,ack=i+1,此时服务器进入SYN_RECV状态。

第三次握手:
客户端收到返回的包进行确认,检查ack是否为i+1(也就是是不是自己第一次发起请求时候产生的随机序号+1),ACK是否为1,如果正确则将标志位ACK置为1,并将ack=j+1(即在服务器序号的基础上加1),seq=i+1(也就是最早的seq序号)发送给服务器端,服务器收到后确认seq=i+i,ACK=1,ack=j+1,服务端验证没有问题随建立连接,并开始打开端口为客户端发送数据。客户端服务端进入ESTABLISHED状态,完成三次握手。

TCP四次断开

在这里插入图片描述
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
不管是客户端还是服务器端都可以调用close之类的函数主动终止一个连接。这里以客户端主动断开连接举例。

第一次握手:
客户端调用close函数,主动发送一个FIN报文给服务器端,用来关闭客户端到服务器的数据传送,此时客户端进入TIME_WAIT1状态。

FIN报文也可能附加用户数据。发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送)但是还可以接收数据。

当调用recv时,如果返回0就表示对端关闭。这个时候通常被关闭端也调用close,然后TCP层发送FIN,继续完成四次握手。如果被关闭端不调用close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。

FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

第二次握手:
服务器收到这个FIN,它发回一个ACK,确认号为收到的序号加1,和SYN一样,一个FIN将占用一个序号,此时服务器进入CLOSE_WAIT状态,客户端进入TIME_WAIT2状态。

第三次握手:
当服务器端也没有要传送的数据时,服务器关闭与客户端的连接,发送一个FIN给客户端A,服务器进入LAST_ACK状态。

第四次握手:
客户端发回ACK报文确认,并将确认号设置为收到序号加1,服务器收到报文并确认成功,服务器端进入CLOSED状态,客户端进入TIME_WAIT(表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。),等待2MSL–120s-240s(1、确保连接可靠地关闭; 即防止最后一个ACK丢失。2、避免产生套接字混淆(同一个端口对应多个套接字),没有收到任何回复,则证明server端已正常关闭,客户端也就关闭了。

常见面试题

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

参考链接

https://blog.csdn.net/soullsj/article/details/80304124
https://blog.csdn.net/qq_38950316/article/details/81087809

  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值