IP协议是通过IP地址来进行定位寻址的,IP地址用来确定要将数据发送给那一台主机;而TCP与UDP协议中则是通过主机端口号来进行确定在主机上的那个应用程序来收发数据。
1. TCP协议提供的服务
1. TCP与UDP都基于网络层的IP协议,但是为应用层提供了完全不同的服务。TCP提供了面向连接的、可靠的字节流服务。面向连接表示在客户端与服务端的两个使用TCP协议的应用程序之间需要首先建立起一个TCP连接,然后才能进行交互数据,而且TCP中也只能进行两个主机之间的通信,无法实现多播和广播。
2. TCP协议保证数据传输可靠性的方式:
(1)应用层传来的数据将被TCP协议分割为合适长度的数据块,这个传递给网络层IP协议处理的数据块被称为报文段。
(2)当TCP发出一个报文段,就会启动一个定时器,等待目的端确认接收该报文段,如果无法在定时器结束前接收确认消息,就会重发报文段。
(3)当一段的TCP收到另一端发送的数据时,就会响应一个确认消息。
(4)TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段并且不确认收到此报文段(希望发端超时并重发)。
(5)既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
(6)IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
(7)TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。 TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止发送数据较快主机致使接收数据较慢主机的缓冲区溢出。
2. TCP首部
1. 应用层的数据进入传输层的TCP协议处理后,会在数据包前添加上TCP协议的首部成为TCP报文段,TCP首部的数据格式如下图所示
- 源端口号和目标端口号:各占16比特,用于确定收发端的应用程序进程。
- 序列号:占32比特,用于标示发送的数据流。
- 确认应答号:占32比特,是指下一次应该收到的数据的序列号,也就是说序列号为确认应答号减1的数据已经成功接收。
- 数据偏移:占4比特,该字段表示TCP首部信息的长度,单位为4字节,也就是说如果数据偏移字段的值为5,那么也就表示TCP首部信息长度为5*4(=20)字节,真正的数据是偏移20字节之后的部分。
- 保留:用于后期扩展,一般设置为0,占4比特。
- 控制位:字段长为8比特,每一比特都代表一个控制标志,从左至右分别是CWR、ECE、URG、ACK、PSH、RST、SYN、FIN。主要是ACK为1时则表示确认序号有效;SYN用来建立连接,为1时表示希望建立连接;FIN为1时表示希望断开连接;RST表示强制断开当前连接,重新建立连接。
- 窗口大小:该字段占用16比特,表示TCP所能发送数据的最大字节数。4
- 校验和:占用16比特,用于计算校验和,校验接收数据是否正确。
- 紧急指针:占用16比特,只有在URG控制位为1时有效,表示本报文段中紧急数据的指针,也就是数据部分的首位到紧急指针所指的位置就是紧急数据。
- 选项:该字段长度不定,但受限于数据偏移字段的大小,所以最大长度40字节,用来提高TCP的传输性能。
窗口大小和吞吐量:TCP通信的最大吞吐量(也就是通常所说的网速)由窗口大小和往返时间决定,如果最大吞吐量为T,窗口大小为W,往返时间为RTT,则
T = W/RTT
假设窗口大小为65535字节,而往返时间为0.1秒,那么最大吞吐量为 65535*8/0.1 = 5242800 bps,相当于5.2Mbps。
2. TCP连接的建立与终止
TCP是面向连接的协议,所以在双方进行数据交互之前,必须先建立连接。而一个TCP连接的建立与断开需要进行交互7个数据包才能完成,也就是常说的“三次握手,4次挥手”。建立与断开连接都是通过TCP首部中的控制字段来实现的,所发送的TCP数据包也都是不包含数据,只有首部信息的。
1. 建立连接:
(1)首先,客户端发送一个TCP报文段请求建立连接,其中的TCP首部信息中的控制字段中的SYN标志位为1,并且首部信息中还包含了想要连接服务端的端口号,初始序号等数据。(第一次握手,客户端发出建立连接的请求,服务端接收)
(2)服务端接收到该连接请求后,服务器响应一个TCP报文段,首部信息中的SYN字段为1,ACK字段也为1,初始序号为服务端的初始序号,而且确认序号字段为客户端发送的初始序号加1,以此进行确认。(第二次握手,服务端接收到客户端发出建立连接的请求,并且确认可以连接,然后返回给客户端一个连接请求)。
(3)客户端在发出建立连接请求后,如果收到了服务端的建立连接请求响应,就表示可以进行连接。客户端响应一个TCP报文段,首部信息中的SYN字段为0,ACK字段为1,确认序号字段为服务端发送的初始序号加1,该TCP报文段发送成功后,TCP连接就成功建立。(第三次握手,客户端接收到服务端发出建立连接的请求,并且确认可以连接,然后发送给服务端一个连接确认,完成TCP连接建立)。
2. 断开连接:建立一个连接需要三次握手,而终止一个连接要经过4次握手。这由TCP的半关闭造成的。可以这么理解,在TCP的两端,每端都有着两条数据通道,一条数据通道是用来接收对方发送的数据,而另一条则是用来向对方发送数据,而我们必须将这两条数据通道全部关闭才算是真正的中断TCP连接,否则如果只中断一条,可能发生另一端仍然在发送数据的情况。
(1)首先,客户端确认数据发送完毕并且服务端也接收完毕后,向服务端发送一条TCP报文段请求终止连接(中断从客户端发送数据到服务端这条通道的连接),报文段首部信息中主要是FIN字段的值为1(第一次挥手)
(2)而服务端在收到客户端的中断请求后就会响应一个首部ACK字段值为1的TCP报文段,客户端收到这条响应之后就意味着从客户端到服务端这个方向的数据通道已经关闭。(第二次挥手)
(3)服务端确认数据发送完毕并且客户端也接收完毕后,向客户端发送一条TCP报文段请求终止连接(中断从服务端端发送数据到客户端这条通道的连接),报文段首部信息中主要是FIN字段的值为1,同时ACK也为1(第三次挥手)
(4)而客户端在收到服务端的中断请求后就会响应一个首部ACK字段值为1的TCP报文段,服务端收到这条响应之后就意味着从服务端到客户端这个方向的数据通道已经关闭。(第四次挥手)至此,TCP连接两端的数据双数据通道已经全部关闭,也就完成了TCP连接的中断。
可以学习一下在Linux系统中tcpdump指令的使用,通过抓包来观察TCP的连接过程。
3. TCP连接状态:TCP连接在建立与中断过程中会有多种状态。如下所示
- LISTEN(S):服务器等待客户端前来连接
- SYN-SEND(C):客户端尝试连接服务器
- SYN-RECEIVED(S):服务器确认首次连接
- ESTABLISHED(SC):服务器和客户端建立连接,双方可以发送数据,为常态。
- FIN-WAIT-1(SC):等待对方关闭
- FIN-WAIT-2(SC):等待对方关闭
- CLOSE-WAIT(SC):等待自己应用程序关闭
- CLOSING(SC):等待对方确认确认自己的关闭
- LAST-ACK(SC):等待最后一次确认消息
- TIME-WAIT(SC):确认对方收到自己的关闭消息,服务器慎用。
- CLOSED(SC):实际是一个不存在的状态,也就是TCP连接中断
4. TCP建立与中断连接锁引起的TCP连接状态变化:
(1)建立连接的三次握手:
- 第1次握手
- 客户端将同步标志位SYN置为1
SYN=1
- 客户端随机产生一个序列号
seq=J
,将该数据包发送给服务器。 - 客户端进行
SYN_SEND
状态,等待服务器确认信息。
- 客户端将同步标志位SYN置为1
- 第2次握手
- 服务器收到数据包后,由同步标志位
SYN=1
得知客户端请求建立连接。 - 服务器将同步标志位SYN和确认标志位ACK置为1
SYN=1
ack=J+1
,并随机产生一个序列号seq = K
,然后将该数据包发送给客户端以确认连接请求。 - 服务器进入
SYN_RCVD
状态
- 服务器收到数据包后,由同步标志位
- 第3次握手
- 客户端收到确认后检查
ack
是否为J+1
,ACK是否为1。如果正确则将确认标志位ACK置为1ack=K+1
,并将该数据包发送给服务器。 - 服务器检查
ack
是否为K+1
并检查ACK是否为1,若正确则连接建立成功。 - 客户端和服务器进入
ESTABLISHED
状态,完成三次握手。随后客户端和服务器之间可以开始传输数据了。
- 客户端收到确认后检查
SYN攻击
在三次握手过程中,服务器发送SYN-ACK之后,收到客户端ACK之前的TCP连接称为半连接(half-open connect),此时服务器处理SYN-REVD状态。当收到ACK后,服务器转入ESTABLISHED状态。
SYN攻击就是客户端在短时间内伪造大量不存在的IP地址,并向服务器不断地发送SYN包,服务器回复确认包,并等待客户端确认,由于源地址不存在。因此服务器需要不断重发直至超时,这些伪造的SYN包将产生时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当服务器上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了。
(2)断开连接的四次挥手:
- 第1次挥手
客户端发送一个FIN用来关闭客户端到服务器的数据传送,客户端进入FIN_WAIT_1状态。 - 第2次挥手
服务器收到FIN后发送一个ACK给客户端,确认序列号为收到序号+1,与SYN相同一个FIN占用一个序列。服务器进入CLOSE_WAIT状态。 - 第3次挥手
服务器发送一个FIN用来关闭服务器到客户端的数据传送,服务器进入LAST_ACK状态。 - 第4次挥手
客户端收到FIN后进入TIME_WAIT状态,然后发送一个ACK给服务器,确认序号为收到序号+1,服务器进入CLOSED状态完成四次挥手。
5. 完整过程状态图:
6. 为什么建立连接需要3次握手,而断开连接需要4次挥手:
因为服务器在LISTEN状态下,收到简历连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,如果我们将ACK和FIN放在一个报文里发送则表示不仅立即确认对方不在发送数据,而且还立即表示自己也不在发送数据,这显然是有问题的,因为收到对方的FIN报文仅仅表示对方不再发送数据了,但是还能接收数据,己方也未必将全部数据都发送给对方了,所以ACK和FIN一般都会分开发送。
7. 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回CLOSE状态:
1. 保证TCP协议的全双工连接能够可靠关闭
如果客户直接关闭,那么由于IP协议的不可靠性或其他网络原因,导致服务器没有收到客户端最后回复的ACK,那么服务器会在超时之后继续发送FIN,此时由于客户端已经关闭了,就找不到与重发的FIN对应的连接,最后服务器会受到RST而不是ACK。服务器会以为是连接错误,然后把问题报告给高层,这样的情况虽然不会造成数据丢失,但是却导致TCP不符合可靠连接的要求。所以,客户端不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN时能够保证双方收到ACK,最后正确的关闭连接。2.保证此次连接的重复数据段从网络中消失
如果客户端直接CLOSED,然后又再次向服务器发起一个新连接,不能保证这个新连接与刚关闭的连接的端口号是不同的,也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是存在特殊情况的出现。假设新连接和已经关闭的老连接的端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才能到达服务器,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair。于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍的MSL,这样保证本次的所有连接都从网络中消失。
8. 完整的状态变迁图: