TCP连接的建立与断开(三次握手与四次挥手)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TCP协议的主要特点 1、TCP是面向连接的传输层协议。应用程序使用TCP协议之前,必须先建立TCP连接。传输数据完成之后需要结束连接。 2、每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。 3、TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。 4、TCP提供全双工通信。TCP允许通信双方的应用程序在任何时候都能发送数据。TCP连接两端都设有发送缓存和接收缓存。用来临时存放双向通信的数据。 在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用程序在合适的时候读取缓存中的数据。 5、面向字节流。TCP中的“流”指的是流入到进程或从进程流出的字节序列。 “面向字节流”的含义:应用程序和TCP的交互时一次一个数据块(大小不等),但TCP把应用程序交下来的数据看成仅仅是一连串的无结构的字节流。TCP不知道所传送的字节流的含义。TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TCP报头字段详解 Sequence Number:报文序号字段,每个TCP连接发出的第一个TCP报文的序号是随机值,之后发送报文的序号是前一个报文的序号+1;序号达到最大值后,下一个报文从0开始;注意:wireshark中优化了报文序号,为了便于观察,第一个TCP报文的序号始终为0,但是观察该字段的详细内容,可以发现实际为随机值 Acknowledgment Number:确认序号;若该TCP连接从未接收过对端发来的TCP报文,值为0;若已接收到TCP报文,则确认序号的值为Sequence Number的值+1 Data Offset:数据偏移,即首部长度(其实就是TCP报头的长度),指出TCP报文段的数据起始处距离TCP报文段的起始处有多远,以32比特(4字节)为计算单位。最多有60字节的首部,若无选项字段,正常为20字节。 Reserved:保留字段,无意义 URG 1比特 紧急指针有效标识。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。 ACK 1比特 确认序号有效标识。只有当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。 PSH 1比特 标识接收方应该尽快将这个报文段交给应用层。接收到PSH = 1的TCP报文段,应尽快的交付接收应用进程,而不再等待整个缓存都填满了后再向上交付。 RST 1比特 重建连接标识。当RST=1时,表明TCP连接中出现严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立连接。 SYN 1比特 同步序号标识,用来发起一个连接。SYN=1表示这是一个连接请求或连接接受请求。 FIN 1比特 发端完成发送任务标识。用来释放一个连接。FIN=1表明此报文段的发送端的数据已经发送完毕,并要求释放连接。 Window 16比特 窗口:TCP的流量控制,窗口起始于确认序号字段指明的值,这个值是接收端正期望接收的字节数。窗口最大为65535字节。 Checksum 16比特 校验字段,包括TCP首部和TCP数据,是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。 Urgent Pointer 16比特 紧急指针,只有当URG标志置1时紧急指针才有效。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。紧急指针指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。 Options 可变 选项字段。TCP协议最初只规定了一种选项,即最长报文段长度(数据字段加上TCP首部),又称为MSS。MSS告诉对方TCP“我的缓存所能接收的报文段的数据字段的最大长度是MSS个字节”。 新的RFC规定有以下几种选型:选项表结束,无操作,最大报文段长度,窗口扩大因子,时间戳。 窗口扩大因子:3字节,其中一个字节表示偏移值S。新的窗口值等于TCP首部中的窗口位数增大到(16+S),相当于把窗口值向左移动S位后获得实际的窗口大小。 时间戳:10字节,其中最主要的字段是时间戳值(4字节)和时间戳回送应答字段(4字节)。 选项确认选项: Padding 可变 填充字段,用来补位,使整个首部长度是4字节的整数倍。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
options字段TimeStamp及其解决的问题 TimeStamp是tcp报文头部的一个可选项,一共占用10个字节:kind(1字节)+length(1字节)+info(8字节) 其中info可以分为2部分:timestamp(4字节)+timestamp echo(4字节) tcp的时间戳主要解决了2个问题 1.计算往返时延RTT (1)timestamp存放的是发送报文时,该主机此时的内核时间T1; (2)timestamp echo则是响应报文时,才会填写的字段,填写的是上一条接收到的tcp报文的timestamp字段所携带的时间戳,即填写T1; (3)当主机收到响应报文时,通过timestamp echo字段得知自己发出的上一条报文的时间戳为T1;而此时主机内核时间为T2 (4)往返时延RTT=T2-T1 2.防止序列号回绕问题 seq number占用了32位,可以表达的范围为0~2^32-1,如果C-S发送数据的时间够长,那肯定是可以用完的;即使2个包的seq number一样,但是时间戳也不可能相同,这样就可以区分出是2个不同的数据包。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TCP状态机:红色实线---client端;蓝色虚线---server端 --------------------------------------------------------------------------------- 共同状态: CLOSED:关闭状态,这个最好理解,啥都不干 ESTABLISHED:连接建立状态,此时客户端和服务器端正在进行通信 客户端: SYN_SENT:当处于关闭状态的客户端准备向服务器发起连接请求时,先发送一个SYN报文到服务器,告诉服务器端,我准备来连接你了,请回答; FIN_WAIT1:客户端获取了想要的数据了,这个时候是时候关闭连接了,在ESTABLISHED状态下,发送一个FIN报文给服务器端,此时等待服务器端的应答; FIN_WAIT2:在FIN_WAIT1的状态下,服务器回了一个ACK包给客户端,意思是你发的连接断开我收到了,不过我现在还有数据没发送完,你再等下; CLOSING:服务器端和客户端都在同时关闭这个连接了,此时客户端只收到一个FIN报文,此时只需要发送一个ACK就行了 TIME_WAIT:服务器发送完数据了,此时就给客户端发送一个FIN保温,意思是现在我这边的内容也发送完了,我们可以正式关闭连接了。此时客户端会发送一个ACK报文给服务器端,告诉服务器端,我收到关闭连接,现在可以确认关闭了,不过我害怕网络有问题,你收不到我发的ACK包,并且我要确定所有的数据都发送完了,所以我需要等一等,确认你确实收到,否则我就要做重传; TIME_WAIT持续时间2MSL:一个MSL在RFC1122中建议为2min,所以2MSL为4min 服务器端: LISTEN:侦听状态,等待来自远方TCP端口的连接请求 SYN_RCVD:收到客户端的连接请求,然后发送SYN,并确认客户端的连接请求,即发送ACK包 CLOSING_WAIT:客户端请求关闭,收到FIN报文,并告诉客户端收到关闭请求了,即发送ACK包; LAST_ACK:数据也发完了,这个时候就开始正式进入关闭状态了。 从图中看出,主动打开的一方的状态转换更为复杂,主要有以下三个分支,现在来逐一的解释一下 FIN_WAIT1-->FIN_WAIT2-->TIME_WAIT:这个状态是客户端连接的一般状态。 FIN_WAIT1-->CLOSING-->TIME_WAIT:客户服务器同时关闭了连接 FIN_WAIT1-->TIME_WAIT:服务器端也没内容发了,发送FIN报文到客户端。进入关闭前的最后状态 根据TCP状态机的理解:在服务器端和客户端都在同时关闭TCP连接时,服务器端和客户端的状态机是不一样的 客户端:ESTABLISHED-->FIN_WAIT1-->CLOSING-->TIME_WAIT 服务器端:ESTABLISHED-->CLOSING_WAIT-->LAST_ACK 也就是说客户端在FIN_WAIT1状态下根据接收到的报文情况进入不同的状态: 1.收到FIN置位报文,进入CLOSING状态 2.收到FIN,ACK置位报文,直接进入到TIME_WAIT状态 3.收到ACK置位报文,进入到FIN_WAIT2状态(一般的4次挥手过程状态)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
半关闭(半关连接)和半连接是不同的概念: 半连接是指发生在3次握手状态,客户端收到服务器端的响应后,不发送ACK报文 半关闭则是一端发送了FIN报文,不会继续发送报文;但此时仍然可以接收报文 客户端执行半连接后,未等待服务器关闭连接就强行退出了。(此时客户端连接由内核接管,可成为孤儿连接,Linux 为了防止孤儿连接长时间停留在内核中,定义了两个内核变量,指定内核能接管的孤儿连接数量和孤儿连接在内核中的生存时间) 孤儿连接带来的问题:占用内存资源 /proc/sys/net/ipv4/tcp_max_orphans #内核中能接管的孤儿连接数目,默认8192 /proc/sys/net/ipv4/tcp_fin_timeout #孤儿连接在内核中的生存时间,默认60秒 TCP全连接和半连接:https://blog.csdn.net/u013128262/article/details/78386777 TCP之半打开和半关闭:https://my.oschina.net/vbird/blog/1528209
TCP三次握手
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TCP三次握手(TCP状态机) 1.初始状态下client和server都处于CLOSED状态; 2.当client需要往server发送数据时,client主动打开连接,server端被动打开连接;打开连接后,分别开始创建传输模块TCB,server端进入LISTEN状态; 3.client端发送第一个数据包用于请求连接,其中flag位SYN置1,seq=x(序列号随机,但是抓包为0),clinet端进入SYN-SENT状态 4.server端收到报文后,若系统当前有资源,则回复确认报文,其中flag位SYN置1,ACK置1,seq=y(序列号随机,但是抓包为0),ack=x+1(对client请求连接的确认);同时server端进入SYN-RCVD状态 5.client端收到server端的确认报文后,需要回复一个确认报文,其中flag位ACK置1,seq=x+1(上一个报文序列号为0,这个当然是1了),ack=y+1(对server端请求连接的确认);同时client端进入ESTABLISHED状态 6.server端收到确认报文后,进入到ESTABLISHED状态
TCP四次挥手
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TCP四次挥手(两个二次握手)(TCP状态机) 1.当client端数据发送完毕,client端主动发送报文请求断开连接,其中flag位FIN置1,seq=n(n为client端发送的上一个数据包的序列号+1);同时client端状态由ESTABLISHED状态切换为FIN-WAIT1状态; 2.server端收到断开请求报文后,回复确认报文,其中flag位ACK置1,ack=n+1(表示对client端第n个报文的确认),seq=m(m为server端发送的上一个数据包的序列号+1);同时服务器端进入CLOSE-WAIT状态 3.client端收到server端的确认报文后,client端进入到FIN-WAIT2状态,等待server端的请求断开连接报文; TCP是全双工通信,,此时client端发起的连接已经结束了,但是server端仍然可以往client端发送数据。 4.server端发送请求断开连接报文,其中flag位FIN置1,ACK置1,seq=z(因为server端在回复client端的断开请求后,可能仍然有其他数据包要发送),ack=n+1(确认号和上次回复客户端的请求释放连接的确认号一样);同时server端进入到LAST-ACK状态 5.client端收到请求后,回复确认报文,其中flag位ACK置1,seq=n+1,ack=z+1;同时client端进入到TIME-WAIT状态;经历时间等待计时器到期后,client端进入到CLOSED状态 6.server端收到确认报文后,进入到CLOSED状态(显然,server端一般先于client端进入到关闭状态) 客户端需要等待2MSL时间才完全结束TCP连接,原因有两个:一、为了保证客户端发送的最后一个确认包能正确到达服务端。因为如果由于网络原因丢失的话,服务端会重新发送连接释放数据包,在等待过程中,如果真的发生这种情况就可以得到处理。客户端每接收到一次服务端发送来的接释放数据包都会重新设置时间等待计时器,然后等待2MSL时间才完全结束TCP连接。二、等待才2MSL时间完全结束TCP连接,可以避免再次开启TCP连接的时候收到上一次TCP连接存在网络中的数据包,显然这样的数据包不是属于本次连接的,是无效的数据包。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
SYN洪水攻击的原理;TCP超时重传相关的两个内核参数;TCP滑动窗口,TCP拥塞控制;centos作为客户端默认使用的端口号范围 -------------------------------------------------------------------------------------------- SYN洪水攻击的原理:在TCP3次握手中,如果客户端向服务端发起TCP请求,服务端也按照正常情况进行响应了,但是客户端不进行第3次握手,这就是半连接。 半连接,会造成服务端分配的内存资源就一直这么耗着,直到资源耗尽 -------------------------------------------------------------------------------------------- TCP超时重传相关的两个内核参数: /proc/sys/net/ipv4/tcp_retries1 #指定在底层IP接管之前TCP最少执行的重传次数,默认为3 /proc/sys/net/ipv4/tcp_retries2 #指定连接放弃前TCP最多可以执行的重传次数,默认为15(一般对应13-30min) -------------------------------------------------------------------------------------------- TCP滑动窗口,TCP拥塞控制 TCP拥塞控制的标准文档RFC5681,详细介绍了拥塞控制的四个部分:慢启动、拥塞避免、快速重传、快速回复。 拥塞控制算法在linux下有多种实现,比如:reno算法、vegas算法、cubic算法等。 /proc/sys/net/ipv4/tcp_congestion_control #当前使用的拥塞控制算法 -------------------------------------------------------------------------------------------- centos作为客户端默认使用的端口号范围 0-1023为著名端口,1024-49151为用户端口or注册端口,分配给程序注册为某应用使用; 49152-65535:动态端口or私有端口,客户端程序随机使用的端口 /proc/sys/net/ipv4/ip_local_port_range #32768 60999;centos默认32768+就是随机端口
参考:
TCP连接的建立与终止 https://www.cnblogs.com/xymqx/p/4452921.html