目录标题
前文
TCP,传输控制协议,是一种可靠的传输层协议。
一个TCP生命周期主要经历三个步骤
1. 建立连接(三次握手)
- client->server. SYN=1,seq=x
- server->client. SYN=1,ACK=1,seq=y,ack=x+1
- client->server. ACK=1,seq=x+1,ack=y+1
2. 传输数据(数据通信)
3. 关闭连接(四次挥手)
- client->server. FIN=1,seq=x
- server->client. ACK=1,ack=x+1,seq=y
- server->client. FIN=1,ACK=1,seq=z,ack=x+1
- client->server. ACK=1,seq=x+1,ack=z+1
1. 三次握手
原则上任何数据传输都无法确保绝对可靠,三次握手只是确保可靠的基本需要。
三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。
刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。
进行三次握手:
第一次握手
客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号seq。此时客户端处于 SYN_SENT (同步已发送)状态。
报文:同步位SYN=1,初始序号seq=x。(不能携带数据,但要消耗掉一个序号)
第二次握手
服务器收到客户端的 SYN 报文之后,将当前客户端的IP、端口之类的加入半连接队列中,并向客户端发送数据包,表明server已经准备好分配资源,处于同步收到(SYN_RCVD) 状态,请求client为建立TCP连接而分配资源。
报文:SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。(不能携带数据)
第三次握手
客户端回复服务器一个 ACK 报文,并分配资源建立连接。server收到这个确认时也分配资源进行连接的建立。此时客户端处于已ESTABLISHED(已建立连接)状态;服务器收到 ACK 报文之后,也处于ESTABLISHED(已建立连接)状态。
报文:ACK=1,确认号ack=y+1,序号seq=x+1。(ACK报文段可以携带数据,不携带数据则不消耗序号。)
1.1 为什么不能用两次握手进行连接?
第一次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
试想如果是用两次握手,则会出现下面这种情况:
如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。
1.2 三次握手过程中可以携带数据吗?
其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
1.3 什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…
1.4 SYN攻击是什么?
在TCP建立连接的时候,第2步,服务器响应客户端的时候,如果步骤3没有给服务器响应,则服务器会一直执行步骤2,发送数据进行尝试连接。直到步骤3客户端给服务器响应后,停止尝试。
这样就给黑客创造了可乘之机,用不存在的地址访问服务器,服务器会一直执行上面的步骤2,给客户端发送连接数据,由于地址是不存在的,不会执行第三步,所以服务器就会在超时时间内,一直尝试发送,这个就是半连接请求。如果模拟成千上万个不存在的地址进行访问,则会快速占满处理队列,不能给正常用户服务。这就是SYN攻击。
1.5 常见的防御 SYN 攻击的方法有如下几种:
- 增加最大半连接数
大量的SYN请求导致未连接队列被塞满,使正常的TCP连接无法顺利完成三次握手,通过增大未连接队列空间可以缓解这种压力。当然backlog队列需要占用大量的内存资源,不能被无限的扩大。 - 缩短超时(SYN Timeout)时间
通过增大backlog队列能防范SYN攻击;另外减少超时时间也使系统能处理更多的SYN请求。我们知道,timeout超时时间,也即半连接存活时间,是系统所有重传次数等待的超时时间总和,这个值越大,半连接数占用backlog队列的时间就越长,系统能处理的SYN请求就越少。为缩短超时时间,可以通过缩短重传超时时间(一般是第一次重传超时时间)和减少重传次数来实现。
超出处理能力时,对新来的SYN丢弃连接(影响用户体验)
1.6 如果已经建立了连接,但是客户端突然出现故障了怎么办
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
2. 四次挥手
TCP 连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务端均可主动发起挥手动作。
刚开始双方都处于ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:
第一次挥手
客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1(终止等待) 状态,并停止再发送数据,主动关闭TCP连接,等待服务端的确认。
报文:FIN=1,序号seq=u。
第二次挥手
服务端收到连接释放报文段后,即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=w),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
报文:ACK=1,确认号ack=u+1,序号seq=w
第三次挥手
服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
报文:FIN=1,ACK=1,序号seq=w,确认号ack=u+1
第四次挥手
客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
报文:ACK=1,seq=u+1,ack=w+1
收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。
在socket编程中,任何一方执行close()操作即可产生挥手操作。
2.1 挥手为什么需要四次
关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
2.2 四次挥手释放连接时,等待2MSL的意义
- 保证客户端发送的最后一个ACK报文段能够到达服务端
为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。 - 防止“已失效的连接请求报文段”出现在本连接中。
客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。
3. Demo(演示三次握手过程)
3.1 请求百度首页
3.2 三次握手
3.3 四次挥手
4. TCP 特点
面向连接的、字节流和可靠传输。
4.1 面向连接的
使用TCP协议通信的双方必须先建立连接,然后才能开始数据的读写,TCP连接是全双工的,即双方的数据读写可以通过一个连接进行。完成数据交换之后,通信双方都必须断开连接以释放资源。TCP协议的这种连接是一对一的,所以基于广播和多播(目标是多个主机地址)的应用程序不能使用TCP。而无连接协议UDP则非常适合于广播和多播。
4.2 TCP传输是可靠的
通过序列号与确认应答提高可靠性。
TCP通过肯定的确认应答ACK实现可靠的数据传输。
- 数据丢失
当发生端将数据发送出去之后会等待对端 的确认应答。如果有确认应答,说明数据已经成功到达对端。在一定的时间内如果没有收到确认应答,发送端就可以认为数据已经丢失,并进行重发。由此,即使产生了丢失,仍然可以保证数据能够到达对端,实现可靠传输。
- 确认应答丢失(重试会导致重复(通过序列号确认重复),重复则丢包)
未收到确认应答并不意味着数据一定丢失,也有可能是对方已经收到,只是返回确认应答在途中丢失,也有可能是其他原因导致确认应答延迟到达,这时都会发生重传。
这时目标主机必须放弃收到重复的包。TCP使用序列号实现这功能,序列号是按照顺序给发送数据的每一个字节都标上号码的编号。接收端查询接收数据TCP首部中的序列号和数据长度,将自己下一步应该接收的序号作为应答返送回去。
这时如果发生重传,接收端收到的序号不是自己上次的应答号,则丢弃该包,并再次发送一个应答包。
4.3 TCP 是基于字节流的协议,无论多大的数据包都可进行传输,并且消息是有序的
TCP的字节流服务的表现形式就体现在,发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系。
当发送端应用程序连续执行多次写操作时,TCP模块先将这些数据放入TCP发送缓冲区中。当TCP模块真正发送数据时,发送缓冲区中等待发送的数据就可能被封装成一个或者多个TCP报文段发出。因此,TCP模块发送出的TCP报文段的个数和应用程序执行的写操作次数之间没有固定的数量关系。
当接收端收到一个或者多个TCP报文段后,TCP模块将他们携带的应用程序数据按照TCP报文段的序号依次放入TCP接收缓冲区中,并通知应用程序读取数据。接收端应用程序可以一次性将TCP接收缓冲区的数据全部读出,也可以分多次读取,这取决于用户指定的应用程序读缓冲区的大小。因此,应用程序执行的读操作次数和TCP模块接收到的TCP报文段个数之间也没有固定的数量关系。(如上图第一行右侧图,执行一次读操作recv,就读出两个TCP报文段)。
综上所述,发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系。
这就是字节流的概念:应用程序对数据的发送和接收是没有边界限制的。
UDP数据报服务则不然(上图第二行所示),发送端应用程序每执行一次写操作,UDP模块就将其封装成一个UDP数据报并发送出去。接收端必须及时针对每一个UDP数据报执行读操作,否则就会丢包。并且,如果用户没有指定足够的应用程序缓冲区来读取UDP数据,则UDP数据将被截断。
5. TCP数据传输
TCP数据传输就是两个人隔空对话,说话的一方都需要确认对方听到了自己的话。具体来说就是:客户端向服务器发送了一条数据,服务器收到了则回复ACK信息。
如果客户端发了数据,而服务器没收到,就是数据丢失了,要客户端重发数据,这是TCP重传。
客户端也可能发多条一样的数据给服务器,服务器就需要进行TCP去重操作。
客户端可以向服务器发数据,服务器也可向客户端发数据,因为TCP链接是双工的。
客户端一连向服务器发多条数据,服务器也不需要收到一条数据就回复一个ACK,而是收到所有消息后,再回复收到所有消息的ACK。
客户端也不能一连发太多数据给服务器,服务器短时间无法消化,两者间需要协商发送与接收速率,这就是TCP窗口大小。
在网络环境中,也存在数据包乱序的现象。同一个来源发出来的不同数据包在网际路由上可能会走过不同的路径,最终达到同一个地方时,顺序就不一样了。操作系统的网络内核模块会负责对数据包进行排序,到用户层时顺序就已经完全一致了。
https://blog.csdn.net/weixin_44865458/article/details/117234974
https://blog.csdn.net/qq_38950316/article/details/81087809
TCP特点:https://blog.csdn.net/YL970302/article/details/83047243
TCP可靠:https://blog.csdn.net/lalalahaitang/article/details/81131699
TCP字节流:https://blog.csdn.net/WILLGOSPURS/article/details/104027601
TCP数据传输:https://www.kuangstudy.com/bbs/1334677642770714625