目录
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,在发送数据前,通讯双方必须在彼此间建立一条连接。所谓的“连接”,其实就是客户端和服务端保存的一份关于对方的信息,如ip地址、端口号等。
TCP可以看成是一种字节流,它会处理IP层或以下层的丢包、重复以及错误问题。在连接的建立过程中,双方需要交换一些参数哦。这些参数可以放在TCP头部。
一个TCP连接由一个4元组构成,分别是两个IP地址和两个端口号。一个TCP连接通常分为三个阶段:连接、数据传输、退出(关闭)。通过三次握手来建立一个连接,通过四次挥手来关闭一个连接。
当一个连接被建立或者终止时,交换的报文段只包含TCP头部,而没有数据。
1、TCP头的构成
- 源端口和目标端口:源端口是该数据包的发送者的端口,目标端口是这包数据要发送到的目标端口。
- 序号seq
- 确认号:接收端收到发送端发来的数据后,会给客户端回一个ACK确认包,确认收到数据了,这就是TCP的ACK机制。只有当ACK标志位为1时,ack的值才有效。
- 标志位:一共6个标志位,即URG、ACK、PSH、RST、SYN、FIN等
①URG紧急 | 当URG=1时,表示此报文段中有紧急数据,是高优先级的数据,应该尽快发送,不用再缓存区排队。 |
②ACK确认 | 当ACK=1,表示确认号ack有效。TCP规定,在建立连接后所有传送的报文段必须把ACK置为1。 |
③PSH推送 | 接收方收到PSH=1的报文段时,就直接将报文发送给应用进程,而不需要等待缓冲区的数据存满。 |
④RST复位 | 当RST=1时,表明TCP链接中出现了严重的错误,必须释放连接,然后再重新确立传输连接。 |
⑤SYN同步 | SYN = 1 表示这是一个连接请求或连接接受报文。当 SYN = 1 而 ACK = 0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN = 1 且 ACK = 1。 |
⑥FIN终止 | 用来释放一个连接。当 FIN = 1时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。 |
2、三次握手
三次握手的本质:确认通信双方收发数据的能力
首先,首先我让信使运输一份信件给对方,对方收到了,那么他知道了我发邮件的能力和他收邮件的能力。
于是他给我回信,我若收到了,我便得知我的发件能力和他的收件能力是可以的,并且他的发件能力和我的收件能力是可以的。
然而,此时的他还不知道他的发件能力和我的收件能力到底可不可以,于是我最后回馈一次,他若收到了,他便知道自己的发件能力和我的收件能力是可以的。
这就是三次握手。
2.1三次握手过程
- 第一次握手:客户端要向服务端发起连接请求,首先客户端要随机生成一个序列号ISN(比如说是100),客户端向服务端发送的报文包含SYN标志位(SYN=1),序列号seq=100
- 第二次握手:服务端受到客户端发送过来的报文后,发现SYN=1,知道这是一个连接的请求。于是将客户端的起始序列号100存起来,并且随机生成一个服务端的起始序列号(比如是300)。然后给客户端回复一段报文,回复包含(SYN=1,ACK=1)、序列号seq=300、确认号ack=101(客户端发送的序列号+1)
- 第三次握手:客户端收到服务端的回复后发现ACK=1并且ack=101,于是知道服务端已经收到了序列号为100的那段报文,同时发现SYN=1,知道了服务端同意了这次连接,于是将服务端的序列号300给存起来。然后客户端回复一段报文给服务端,报文含ACK标志位(ACK=1),ack=301(300+1),seq=101(注意第一次握手时发送报文是占用一个序列号的,所以第三次握手的序列号是101,但是要注意的是不携带数据的ACK报文不占用序列号的,所以后面第一次正式发送数据时seq还是101)。当服务器收到报文发现ACK=1并且 seq=301,就知道客户端收到序列号为300的报文了,就这样客户端和服务端通过TCP建立了连接。
2.2三次握手的好处
①确认双方收发能力是否正常,确认收发的可靠性。如果只是两次握手,服务端不知道自己的发送能力如何,也不知道客户端是否收到自己的报文,不能保证传输的可靠性。
②三次握手可以防止已经失效的连接请求报文段突然被传送到服务器端,导致服务器错误的建立连接,造成服务器资源的浪费。
3、什么是SYN包攻击?
SYN 包攻击是指利用 TCP 需要三次握手的特性,攻击者伪造 SYN 报文向服务器发起连接,服务器在收到报文后用 ACK 应答,但之后攻击者不再对该响应进行应答,造成一个半连接。假设攻击者发送大量这样的报文,那么被攻击主机就会造成大量的半连接,服务器支持的总的连接数是有限的,最终会导致服务器连接资源耗尽,导致正常的 SYN 请求因为队列满而被丢弃,使得正常用户无法访问。
服务器侧有一个半连接队列和一个全连接队列。半连接队列是指,服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把这种状态下的请求连接放在一个队列里,我们把这种队列称之为半连接队列。对于全连接队列,完成三次握手后建立起的连接就会放在全连接队列中。
4、四次挥手
四次挥手的目的是关闭一个连接。
Q:为什么挥手是四次?
A:因为客户端发出FIN报文时只能说明客户端没有数据要发送了,但是此时可能服务端的数据还没有发完。所以首先服务端要给客户端一个报文表示自己以及收到服务端的FIN请求了。然后服务端继续进行数据发送,当数据完成时,客户端向服务端发送一个FIN报文,之后收到客户端的确认报文后,才会结束TCP连接。
比如客户端初始化的序列号ISA=100,服务端初始化的序列号ISA=300。TCP连接成功后客户端总共发送了1000个字节的数据,服务端在客户端发FIN报文前总共回复了2000个字节的数据。
- 第一次挥手:当客户端的数据都传输完成后,客户端向服务端发出连接释放报文(当然数据没发完时也可以发送连接释放报文并停止发送数据),释放连接报文包含FIN标志位(FIN=1)、序列号seq=1101(100+1+1000,其中的1是建立连接时占的一个序列号)。需要注意的是客户端发出FIN报文段后只是不能发数据了,但是还可以正常收数据;另外FIN报文段即使不携带数据也要占据一个序列号。
- 第二次挥手:服务端收到客户端发的FIN报文后给客户端回复确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=1102(客户端FIN报文序列号1101+1)、序列号seq=2300(300+2000)。此时服务端处于关闭等待状态,而不是立马给客户端发FIN报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完。
- 第三次挥手:服务端将最后数据(比如50个字节)发送完毕后就向客户端发出连接释放报文,报文包含FIN和ACK标志位(FIN=1,ACK=1)、确认号和第二次挥手一样ack=1102、序列号seq=2350(2300+50)。
- 第四次挥手:客户端收到服务端发的FIN报文后,向服务端发出确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=2351、序列号seq=1102。注意客户端发出确认报文后不是立马释放TCP连接,而是要经过2MSL(最长报文段寿命的2倍时长)后才释放TCP连接。而服务端一旦收到客户端发出的确认报文就会立马释放TCP连接,所以服务端结束TCP连接的时间要比客户端早一些。
为什么第四次挥手后要等待2MSL?
考虑到丢包问题,如果第四次挥手报文丢失,服务端没有收到确认ACK的报文就会重新发送第三次挥手的报文,这样报文一去一回的最长时间就是2MSL,所以要等待这么长时间确定服务端确实收到客户端确认报文了。
参考文献: