在四层网络结构中,TCP和UDP是属于传输层,IP是网络层。TCP虽然是面向字节流的,但是TCP传送的数据单元却是报文段。TCP报文段分为首部和数据两部分,而首部字段的作用显示出了TCP报文的特性。理解好TCP报文的相关知识,对于理解TCP连接的三次握手有帮助,同时对Socket编程的学习也会有促进作用。


TCP报文:

TCP报文段首部前20个字节是固定的,可能后面有4n个字节是根据需要增加的,所以TCP首部有20+4n个字节,最少20个字节。

TCP报文首部分别为:1.源端口  2.目的端口  3.序号Seq  4.确认号Ack  5.数据偏移  6.保留  7.紧急URG  8.确认ACK  9.推送PSH  10.复位RST  11.同步SYN  12.终止FIN  13.窗口  14.紧急指针  15.选项(长度可变)16.填充部分


TCP报文如下图:

TCP首部

        TCP报文段的数据部分        

IP报文分为IP首部和IP数据报的数据部分,其中IP数据报的数据部分包裹着TCP报文,一种层层封装的结构。  


TCP首部如下图所示:

TCP报文讲解_TCP数据报

TCP报文首部到第五行,即选项字段之前的字节是固定的,共20个字节,后面4n个字节根据需要增加。   ​1.源端口和2.目的端口:​各占两个字节,TCP分用功能也是通过端口实现的。  

3.序号Seq:​占4字节,共有2^32个序号,下一个又回到0。首部中这个序号字段Seq指的是本报文段所发送的数据的第一个字节的序号,比如某一报文段的字段值301,而携带的数据共有100个字节,则表明报文段的数据的第一个字节序号为301,最后一个字节的序号为400,因为TCP是面向字节流的。

4.确认号Ack:​占4个字节,表示期望收到对方下一个报文段的第一个数据字段的序号。如果说序号Seq是发送所携带数据的第一个字节的序号,那么确认号Ask是即将接收到的报文的数据的第一个字节的序号。

5.数据偏移:占4位,​表示TCP报文段的数据起始处距TCP报文段的起始处有多远,即TCP首部长度,即20+4n个字节。由于占4位,可以表示到2^4为16,能够表示的最大十进制数字是15,因此数据偏移的最大值为60字节,也就是说TCP报文首部最小为20字节,最大为60字节,也就说n最大为10。

6.保留:​占6位,保留为今后使用,目前置为0。

7.紧急URG:​当URG=1时,表明紧急指针字段有效,他告诉系统此报文段中有高优先级的数据,应优先传送。

8.确认ACK:​当ACK=1时确认号Ack才有效,ACK=0时,确认号无效。TCP规定,在建立连接后所有传送的报文段都必须把ACK置1。

9.推送PSH:​两个应用进程进行交互式通信,有时在一段的应用进程希望在键入一个命令后立即能够收到对方的响应,可以用到TCP的推送操作。PSH=1表示立即创建一个报文段并发送出去,当接收方TCP收到PSH=1的报文段时,就会尽快的交付给应用进程。

10.复位RST:​当RST=1时,表明TCP连接中出现严重错误,比如主机崩溃等,必须释放连接,然后再重连。RST=1还可用来拒绝一个非法的报文段,或拒绝打开一个连接。

11.同步SYN:​在连接建立时用来同步序号的,当SYN=1时且ACK=0时,表示这是一个连接请求,若对方同意连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN=1表示这是一个连接请求或者连接接受报文。

12.终止FIN:​作用用来释放一个连接,当FIN=1时,表明此报文段发送方的数据已经发送完毕,并要求释放运输连接。

13,.窗口:​占2个字节,表示发送报文段的那一方的一个接收窗口所能允许接受到对方发送的数据量的大小,比如说确认号Ack为701,窗口为1000,则表示下一次接收,从701序号开始,发送报文段的一方还有接收1000个字节数据。窗口值经常动态变化着。

14.检验和:​占2个字节,检验的范围包括首部和数据部分。

15.紧急指针:​占2字节。紧急指针仅指在URG=1时才有意义,它指出本报文段中紧急数据的字节数。当所有紧急数据都处理完毕,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为0也可以发送紧急数据。

16.选项:​长度可变,最长可达40字节,也可以没有,即0字节。


TCP的三次握手简述:

SYN是同步控制字段,ACK是确认控制字段。Seq是序号,Ack是确认号

第一次握手:​client→Server,SYN=1且Seq=J

第二次握手:​server→client,SYN=1,ACK=1且Seq=K,Ack=J+1

第三次握手:​client→server,SYN=1,ACK=1且ACK=K+1


TCP的三次握手:

第一次握手:​client端SYN=1,随机产生一个值Seq=J,数据报发送给server端,client端进入​SYN_SENT​状态,等待server确认。

第二次握手:​server端接收到数据报后通过SYN=1知道这是一个请求连接的数据报,于是server端的ACK和SYN都置为1,表示要发送一个连接接受的报文,并使确认号Ack生效,Ack=J+1,随机产生一个值Seq=K,于是将数据报发送给client端来确认连接请求。server端进入​SYN_RCVD​状态。

第三次握手:​client端收到确认之后,检查Ack是否为J+1和ACK是否为1,若是,则将ACK置为1,Ack=K+1,并将该数据包发送给server端,server端检查Ack是否等于K+1和ACK是否等于1,若是,则建立连接成功,client和sever都进入​ESTABLISHED​状态,之后client端和sever端就可以开始传递数据了。


SYN攻击:

​三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接,也就是第二次握手之后和第三次握手之前的一段时间,​​此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:

#netstat -nap | grep SYN_RECV


四次握手:

四次握手即终止TCP连接,指断开一个TCP连接时需要经过四次包的发送,在socket编程中一般都是通过close方法来触发的。TCP可以全双工通信,因此每个方向都要单独关闭。

第一次握手:​client发送一个FIN,表示关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

第二次握手:​Server收到FIN后,发送一个Ack给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。表示答复第一次握手。

第三次握手:​Server发送一个FIN,表示关闭Server到Client的数据传送,Server进入LAST_ACK状态。

第四次握手:​Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个Ack给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

<span style="color:#666666">上面是client方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况。
</span>

可能有人会问,为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。