TCP协议简介
TCP全称Transmission Control Protocol,数据传输控制协议。它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
互联网上的数据通过应用层到物理层 层层封装(添加头部信息),由物理层传输到数据接收端,再层层解封装,最后到到达数据接收端——应用层。
常用的熟知端口号
应用程序 | FTP | TFTP | TELNET | SMTP | DNS | HTTP | SSH | MYSQL |
---|---|---|---|---|---|---|---|---|
熟知端口 | 21,20 | 69 | 23 | 25 | 53 | 80 | 22 | 3306 |
传输层协议 | TCP | UDP | TCP | TCP | UDP | TCP | TCP | TCP |
TCB是啥?
Socket包含两部分,一个是IP地址,一个是端口号。同一个设备可以对应一个IP地址,但不同的管道用不同的端口号区分,于是同一个设备发送给其他不同设备的信息就不会产生混乱。在同一时刻,设备可能会产生多种数据需要分发给不同的设备,为了确保数据能够正确分发,TCP协议用一种叫做TCB(Transmission Control Block,传输控制块)的数据结构把发给不同设备的数据封装起来。
一个TCB数据块包含了数据发送双方对应的socket信息以及拥有装载数据的缓冲区。在两个设备要建立连接发送数据之前,双方都必须要做一些准备工作,分配内存建立起TCB数据块就是连接建立前必须要做的准备工作。我们还需要了解的一点是TCP连接的建立方式,由于TCP协议建立在服务器–客户端的模式之上,因此对于两种不同角色的设备,他们发起连接的方式不一样。
客户端发起连接的方式叫主动打开(Active Open)。也就是客户端需要主动向服务器发送消息,表达自己想建立数据连接的请求。服务器发起连接的方式叫被动打开(Passive Open),通常服务器在没有客户端主动请求的时候不知道当前有哪个设备想向它发起连接,因此它只能构建一个端口并监听该端口,等待客户端从该端口向它发起连接请求。在OPEN(打开)阶段无论是客户端还是服务器都需要准备好TCB数据结构,但由于服务器不知道要连接它的客户端的地址信息,因此在构建TCB模块时会默认将客户端对应的Socket数据初始化为0。
当客户端和服务器端双方都把自己的Socket和TCB数据结构准备好后,双方就可以进入所谓的“三次握手”TCP连接建立过程。
- 更详细的TCB数据结构源码可到此处查看:https://blog.csdn.net/sunshineywz/article/details/101867600
TCP的头部信息
TCP头部包含20个字节的固定首部和长度可变的最多40个字节的填充信息部分(可没有)。
最开头的四个字节为
源端口
和目的端口
,记录端口信息,再加上位于网络层的IP信息头部中包含的源地址和目标地址信息,可唯一确定一个TCP连接。再往后的四个字节为
序号
(SYNchronize sequence numbers),值范围为0到2的32次方减1,标示TCP数据包中第一个字节所存储内容的序号,SYN超过最大值则回到0重新开始,做的是一个取余(mod)运算。确认号
(Acknowledgment number)包含发送确认的一端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志为1时该确认序列号的字段才有效。主要用来解决不丢包的问题;数据偏移
(offset)表示TCP头部的32bit字的数目,offset占四个bit位,故其最大值为15,这也表示TCP头部的最大字节数为4*15=60个字节。你看到的竖排文字(URG ACK PSH RST SYN FIN)这些都是
TCP flags
,用于操控TCP状态机,表示当前的状态。
URG
:此标志表示TCP包的紧急指针域有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据;
ACK
:此标志表示应答域有效,就是说前面所说的TCP确认号将会包含在TCP数据包中;ACK这个Flag有两个取值:0和1,为1的时候表示应答域有效,反之无效;
PSH
:这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给应用程序,而不是在缓冲区中排队;
RST
:这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包;
SYN
:表示同步序号,用来建立连接。SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个SYN=1,ACK=0的数据包,如果对方主机响应了一个SYN+ACK数据包回来 ,就表明这台主机存在这个端口。
FIN
: 表示双方的数据传送完成,没有数据可以传送了,四次挥手,发送FIN标志位的TCP数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。
再后面两个字节为
窗口
大小(window),也就是TCP中的滑动窗口——应用于TCP的流量控制。比较复杂,以后再补充。校验和
提高了TCP数据传输的可靠性;紧急指针
和URG标志位配合使用;再往后就是可选区域
,非必须。
TCP三次握手与四次挥手
TCP为什么要进行三次握手,而不是两次握手?
这个问题在计算机网络的教材中可以找到答案。在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是,为了防止已失效的连接请求突然又传送到了服务器端,不进行第三次握手可能会产生服务器资源不必要的消耗。
试想,如果只经过两次握手即建立TCP连接会怎样?
栗子:一个已失去时间有效期的TCP连接请求报文被发送到服务器端,服务器此时并不知道这个报文在它的发送者——客户端那里已失效,服务器对这个请求报文做出回应,并切换到SYN_RCVD
状态,建立TCP连接,而这个回应报文发送到客户端时,客户端并不会处理此请求,因为它发出去的请求报文早已过期,它没有处于SYN_SEND
状态,而服务器误以为客户端已经处于此状态,这样就会造成服务器资源的不必要损耗,由此得出了“三次握手”建立TCP连接的方法。
通过三次握手,服务器在接收到失效请求时也是做出了回应,但这个回应(SYN/ACK包)如果在一定时间内没有收到回应,则不会建立TCP连接,资源被释放;如果服务器收到了来自客户端的回应,则建立TCP连接。
TCP的三次握手
“三次握手”的作用就是保证客户端、服务器端双方都能明确自己和对方的收、发能力是正常的。
最开始的时候客户端和服务器都是处于CLOSED可用状态。开始建立TCP连接之前,服务器端的一个端口已经被动打开(passive open),用于监听来自客户端的请求,这样,客户端的端口可以主动打开(active open),发送请求到服务器端,寻求建立连接。
第一次握手:客户端向服务器端发送SYN包,SYN标志位(Flag)置1,包的序号为客户端的ISN(Initial Sequence Number),包发出后,客户端进入
SYN_SEND
状态,客户端知晓客户端的发包能力正常;第二次握手:服务端接收客户端发来的连接请求,以SYN+ACK包做出回应,包的SYN标志位置1,ACK标志位置1,序号为服务端的ISN(与客户端的ISN不同),确认码为客户端的ISN加1,包发出后,服务端进入
SYN_RCVD
状态,服务端知晓客户端的发包能力正常、服务端的发包能力正常,服务器的收包能力正常;第三次握手:客户端接收服务端发来的响应,以ACK包做出回应,包的ACK置1,序号为客户端的ISN+1,确认码为服务端的ISN+1,此包发出后,客户端转为
ESTABLISHED
状态,客户端知晓服务端的发包能力正常,服务端的收包能力正常、客户端的收包能力正常。服务器接收到这个包后,也进入ESTABLISHED
状态,服务端知晓客户端的收包能力正常,至此TCP连接被成功建立,可以进行数据传输。
三次握手中的数据包只有TCP头部信息,没有主体信息。
TCP协议规定,ACK报文段(ACK标志位置1的报文)可以携带数据,但是如果不携带数据则不消耗序号,即下一个报文的序号仍为ACK报文的序号。
TCP协议规定,SYN报文段(SYN标志位置1的报文,SYN+ACK报文也属于这种情况)不能携带数据,但需要消耗掉一个序号,下一个发送的报文序号需要+1。
为什么TCP要四次挥手才能互相断开TCP连接?
简单来说,处于传输层的TCP协议是一个可靠的协议,而在它下面,处于网络层的IP(Internet Protocol,网络协议)协议是不可靠的,TCP可靠性的具体体现之一就是四次挥手,不会出现不必要的错误和资源损耗。
那四次分手又是为何呢?TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议,全双工模式。
单工(Simplex)通信是指消息只能单方向传输的工作方式。例如遥控、遥测。
半双工(Half Duplex)数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。
全双工(Full Duplex)通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。
这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但这时主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快地断开这次TCP连接。
可以看到,如果不经过四次挥手,是不能断开连接的,因为对方那里可能还有数据没发送。只有双方互相确认(ACK)了彼此的FIN报文,TCP连接才能断开,TCP才靠得住。
TCP四次挥手
客户端和服务器端均可以发出FIN报文,即发出断开连接的请求——表示自己没有数据要发送给对方了,谁先发的谁即为主动方。
[图片上传失败...(image-308658-1606361336319)]
第一次挥手:主动方 向 被动方 发出FIN报文,序号为u(等于前面已经传送过来的数据的最后一个字节的序号加1),确认码为被动方的ISN+1,此时主动方进入
FIN_WAIT_1
状态,等待被动方确认。第二次挥手:被动方发送ACK报文给主动方,确认主动方可以关闭连接,这个报文中序号为v(由之前已发出的数据序列决定),确认码为u+1,被动方发出ACK报文后进入
CLOSE_WAIT
状态,检查自身是否还有数据发送给对方——这时候被动方处于半关闭状态,即主动方已经没有数据要发送了,但是被动方若发送数据,主动方依然要接收。这个状态还要持续一段时间,也就是整个CLOSE_WAIT
状态持续的时间。主动方接收到ACK消息即转入FIN_WAIT_2
状态,继续等待被动方发来的FIN报文。第三次挥手:还是被动方,被动方检查自身没有数据要发给主动方了,而在这之前被动方很可能又发送了一些数据给主动方,假定此时的序号为w。被动方发送FIN+ACK报文给主动方,序号为w,确认码为u+1,发出后,被动方转入
LAST_ACK
状态,等待主动方关于断开连接的确认。第四次挥手:主动方收到被动方发来的FIN报文,回应一个ACK报文,序号为u+1,确认码为w+1。发出后,主动方转入
TIME_WAIT
状态,将在2∗MSL( Maximum Segment Lifetime,最长报文段寿命)后断开连接,随后进入CLOSED
可用状态;被动方接收到最后的ACK报文后,随即断开连接,进入CLOSED
可用状态。
TCP规定,FIN报文段要消耗一个序号,即下一个把报文的序号需+1。
服务器结束TCP连接的时间要比客户端早一些。
①
FIN_WAIT_1
(主动方):FIN_WAIT_1
和FIN_WAIT_2
状态都表示等待对方的FIN报文。这两种状态的区别是:
FIN_WAIT_1
状态实际上是当SOCKET在ESTABLISHED
状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1
状态。而当对方回应ACK报文后,SOCKET方进入到FIN_WAIT_2
状态。在实际情况下,无论对方何种状况,都会马上回应这个FIN一个ACK报文,所以
FIN_WAIT_1
状态一般是比较难捕捉到的,而FIN_WAIT_2
状态常可以用netstat命令查看。②
FIN_WAIT_2
(主动方):FIN_WAIT_2
状态下的SOCKET,表示半连接,也即主动方要求断开TCP连接,但主动方仍需要接收对方发来的FIN报文,并回应一个ACK报文。③
CLOSE_WAIT
(被动方):这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT
状态。接下来你真正需要考虑的,是查看你是否还有数据发送给主动方,如果没有的话,那么你也就可以 close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在
CLOSE_WAIT
状态下,需要完成的事情是检查你还有没有数据要发给对方,没有就关闭连接,发送FIN报文告知主动方。④
LAST_ACK
(被动方): 是被动方在发送FIN报文后处于的一个状态,即被动方已确认无数据需要发送给主动方,可以关闭连接。被动方最后等待主动方的ACK报文。当收到ACK报文后,被动方即进入
CLOSED
可用状态了。⑤
TIME_WAIT
(主动方): 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后主动方即可回到CLOSED
可用状态了。如果
FIN_WAIT_1
状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT
状态,而无须经过FIN_WAIT_2
状态。(看图)⑥
CLOSED
: 表示连接已断开。