TCP报文首部的后面,接着的是数据部分,不过数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据,比如在处理超时的过程中,也会发送不带任何数据的报文段。
总体来说,TCP协议的可靠性,主要通过以下几点来保障:
(1)应用数据分割成TCP认为最适合发送的数据块。这部分是通过MSS(最大数据包长度)选项来控制的,通常这种机制也被称为一种协商机制,MSS规定了TCP传往另一端的最大数据块的长度。值得注意的是,MSS只能出现在SYN报文段中,若一方不接收来自另一方的MSS值,则MSS就定为536字节。一般来讲,MSS值还是越大越好,这样可以提高网络的利用率。
(2)重传机制。设置定时器,等待确认包,如果定时器超时还没有收到确认包,则报文重传。
(3)对首部和数据进行校验。
(4)接收端对收到的数据进行排序,然后交给应用层。
(5)接收端丢弃重复的数据。
(6)TCP还提供流量控制,主要是通过滑动窗口来实现流量控制。
至此TCP协议的数据帧格式介绍完了。接下来开始为大家重点介绍:TCP传输层的三次握手建立连接,四次挥手释放连接。

TCP的三次握手

TCP连接的建立时,双方需要经过三次握手,而断开连接时,双方需要经过四次分手,那么,其三次握手和四次分手分别做了什么呢?又是如何进行的呢?
通常情况下,建立连接的双方,由一端打开一个监听套接字(ServerSocket)来监听来自请求方的TCP(Socket)连接,当服务器端监听开始时,必须做好准备接受外来的连接,在Java中该操作通过创建一个ServerSocket服务监听套接字实例来完成,此操作会调用底层操作系统(如Linux)的C代码中三个函数socket()、bind()、listen()
来完成。开始监听之后,服务器端就做好接受外来连接的准备,如果监听到建立新连接的请求,会开启一个传输套接字,称之为被动打开(Passive
Open)。
一段简单的服务端监听新连接请求,并且被动打开(Passive
Open)传输套接字的Java示例代码,具体如下:
public class SocketServer { public static void main(String[] args) { try { // 创建服务端socket ServerSocket serverSocket = new ServerSocket(8080); //循环监听等待客户端的连接 while(true){ //监听到客户端连接,传输套接字被动开启 Socket socket = serverSocket.accept(); //开启线程进行连接的IO处理 ServerThread thread = new ServerThread(socket); thread.start(); ...... } } catch (Exception e) { // 处理异常 e.printStackTrace(); } } }
客户端在发起连接建立时,Java代码通过创建Socket实例,调用底层的connect(…)方法,主动打开(Active
Open)Socket连接。套接字监听方在收到请求之后,监听方和发起方(客户端)之间就会建立一条的连接通道,该通道由双方IP和双方端口所唯一确定。
一段简单的客户端连接主动打开(Active Open)的Java示例代码,具体如下:
public class SocketClient { public static void main(String[] args) throws InterruptedException { try { // 和服务器创建连接 Socket socket = new Socket("localhost",8080); // 写入给监听方的输出流 OutputStream os = socket.getOutputStream(); ….. // 读取监听方的输入流 InputStream is = socket.getInputStream(); ….. } catch (Exception e) { e.printStackTrace(); } } }

三次握手过程

TCP连接的建立时,双方需要经过三次握手,具体过程如下:
(1)第一次握手:Client进入SYN_SENT状态,发送一个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产生的一个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带一个MSS(最大报文段长度)可选项的值,表示客户端发送出去的最大数据块的长度。
(2)第二次握手:Server端在收到SYN帧之后,会进入SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要目的在于通知Client,Server端已经收到SYN消息,现在需要进行确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment
Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端生成的SN序号;SYN+ACK帧的MSS(最大报文段长度)表示的是Server端的最大数据块长度。
(3)第三次握手:Client在收到Server的第二次握手SYN+ACK确认帧之后,首先将自己的状态会从SYN_SENT变成ESTABLISHED,表示自己方向的连接通道已经建立成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment
Number)值被设置为Server端的SN序列号+1。还有一种情况,Client可能会将ACK帧和第一帧要发送的数据,合并到一起发送给Server端。
(4)Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进入ESTABLISHED状态,至此,Server方向的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成。

三次握手的图解

三次握手的交互过程,具体如下图所示:
图:TCP建立的连接时三次握手示意图
Client和Server完成了三次握手后,双方就进入了数据传输的阶段。数据传输完成后,连接将断开,连接断开的过程需要经历四次挥手。

TCP的四次挥手

业务数据通信完成之后,TCP连接开始断开(或者拆接)的过程,在这个过程中连接的每个端的都能独立地、主动的发起,断开的过程TCP协议使用了四路挥手操作。

四次挥手具体过程

四次挥手具体过程,具体如下:
(1)第一次挥手:主动断开方(可以是客户端,也可以是服务器端),向对方发送一个FIN结束请求报文,此报文的FIN位被设置为1,并且正确设置Sequence
Number(序列号)和Acknowledgment
Number(确认号)。发送完成后,主动断开方进入FIN_WAIT_1状态,这表示主动断开方没有业务数据要发送给对方,准备关闭SOCKET连接了。
(2)第二次挥手:正常情况下,在收到了主动断开方发送的FIN断开请求报文后,被动断开方会发送一个ACK响应报文,报文的Acknowledgment
Number(确认号)值为断开请求报文的Sequence Number
(序列号)加1,该ACK确认报文的含义是:“我同意你的连接断开请求”。之后,被动断开方就进入了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知高层的应用进程,对方向本地方向的连接已经关闭,对方已经没有数据要发送了,若本地还要发送数据给对方,对方依然会接受。被动断开方的CLOSE-WAIT(关闭等待)还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
主动断开方在收到了ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。
(3)第三次挥手:在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截止后,被动断开方会向主动断开方发送一个FIN+ACK结束响应报文,表示被动断开方的数据都发送完了,然后,被动断开方进入LAST_ACK状态。
(4)第四次挥手:主动断开方收在到FIN+ACK断开响应报文后,还需要进行最后的确认,向被动断开方发送一个ACK确认报文,然后,自己就进入TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,如果期间没有收到其他报文,则证明对方已正常关闭,主动断开方的连接最终关闭。
被动断开方在收到主动断开方的最后的ACK报文以后,最终关闭了连接,自己啥也不管了。