TCP标志位


第一次握手:客户端发送SYN包至服务器,并进入SYN_SENT状态,等待服务器确认
第二次握手:服务器收到客户端的SYN包,发送一个ACK,同时发送自己的SYN,此时服务器进入SYN_RCVD状态
第三次握手:客户端接收到服务器发送的SYN+ACK后,进入ESTABLISHED状态,并发送服务器SYN包的确认ACK,服务器接收到客户端ACK后,进入ESTABLISHED状态
当客户端和服务器都进入ESTABLISHED状态后,客户端和服务器之间就可以开始双向传递数据了。
- FIN是客户端或服务器正常关闭tcp连接发的包,发送缓冲区Send-Q数据发完才会发FIN
- RST是客户端或服务器异常关闭tcp连接发的包,通常都是主机收到不存在的tcp通道的tcp报文,主机“被迫”回复RST告知对方tcp连接异常,收到RST的一方就会关闭这个socket,释放所有socket信息,且不会回复ACK
“TCP首部字段有6个是flags,每个标志位有特殊的含义,它们可以单独存在,也可以同时存在。对于接收方,不同的标志位代表不同的意思,需要做正确的处理
”
Client -> SYN, SEQ=100 Client <- SYN, ACK, SEQ=700, ACK=101 <- Server Client -> ACK = 701, SEQ=101 [50 Bytes of data] Client -> ACK = 701 [Again, didn't receive sth from server yet], SEQ = 151
其实在握手和结束时确认号应该是对方序列号加1,传输数据时则是对方序列号加上对方携带应用层数据的长度.
另外,如果对方没有数据过来,则自己的确认号不变,序列号为上次的序列号加上本次应用层数据发送长度.
seq是序列号,就是data stream字节序号这是为了连接以后传送数据用的,ack是对收到的数据包的确认,值是等待接收的数据包的序列号。都是字节
在第一次消息发送中,A随机选取一个序列号作为自己的初始序号发送给B;第二次消息B使用ack对A的数据包进行确认,因为已经收到了序列号为x的数据包,准备接收序列号为x+1的包,所以ack=x+1,同时B告诉A自己的初始序列号,就是seq=y;第三条消息A告诉B收到了B的确认消息并准备建立连接,A自己此条消息的序列号是x+1,所以seq=x+1,而ack=y+1是表示A正准备接收B序列号为y+1的数据包。
seq是数据包本身的序列号;ack是期望对方继续发送的那个数据包的序列号。
ack和seq都是两个整型变量,表示序列号;ACK、SYN是指标志位,是用来标识这个包的类型的,这两者不是同一个东西。
1:SYN/ACK:即序列号与确认号,允许连接的双方在发送数据时,同时对对方的数据段进行确认
1) SYN:数据包都是按字节编号,如果前一个数据包的序列号是0而该数据段的数据区携带了100B,则下一个数据包的序列号应该是100;初始序列号必须是随机产生的,这个值在建立连接的过程中指定
注意:下一个数据包指的是本机下一次向对方发送的数据包,并不包括对方回复的数据包
2) ACK:确认收到的数据段的序列号,表示它准备接受的下一个数据包的序列号
举一个例子: Client与Server通信
三次握手建立连接
1) Client发起连接,初始序列号假设为x,则SEQ=X+0,此时ACK并不置位
2) Server进行回复,ACK位置1,且ACK=X+1(表明下一个要接收的数据包的序列号),同时发送自己的初始序列号Y
3) Client进行确认,ACK位置1,且ACK=Y+1(表明下一个要接收的数据包的序列号),同时自己的序列号为SEQ=X+1;由于这里TCP数据包并没有长度,所以下一次发送的数据包SEQ仍然为X+1
接下来Client给Server发送数据包
4) 假设数据包长度为256,则SEQ=X+1,ACK=Y+1,且data length=256
5) Server进行回复,SEQ=Y+1,ACK=X+257(表明下一个要接收的数据包的序列号)
2:滑动窗口机制:简单的确认重发机制(发送方发出一个数据后就停下来等待对方的确认)效率低下——让发送方可以连续发送多个数据,然后等待接收方的确认
1) TCP数据根据状态划分为三种类型:已发送、待发送以及发送中
2) 窗口的大小决定了数据传输的效率,需要根据网络实时情况来决定其大小
——TCP的滑动窗口机制:基本的数据单位不是数据段,而是字节;每个窗口用三个指针表示,第一个指向窗口的第一个字节,第二个指向窗口中马上要发送的字节,第三个指向窗口的最后一个字节。
1) 累积确认:接收方对某个编号进行确认,该编号之前的数据必须都已接收到了,从而有时候前面窗口的数据没有接受到,就必须暂存数据,并在接收到前面的其他数据后进行累积确认
a) 优点:允许发送方在对数据进行重发时发送更多的数据,无需对丢失的确认进行重发;为确认数据段也会丢失,但只要下一个确认能够到达对方就无需对前一个确认进行重发
b) 缺点:若第一个窗口的数据丢失,则收到的确认好一直都是关于之前数据的确认,得不到关于新发送的数据包的确认;从而无法确定从该窗口开始的数据包哪些成功发送,哪些丢失了
2) 发送方窗口不能大于接收方窗口,凡是ACK 置为1 的数据段都应该将自己接收窗口的当前大小填入TCP数据段头部的窗口字段中,通知给发送方
3) 拥塞控制机制:慢启动和成倍减少;TCP 协议实际发送窗口的大小应该是拥塞窗口和接收方接收窗口中小的一个
a) 成倍减少机制:当TCP 发现数据段丢失(即超时重发)时,就将拥塞窗口的大小减少一半(但其大小至少应为1 ) ,对于减小窗口后仍在发送窗口中的数据段,将其重发的超时值延长一倍。
b) 慢启动机制:在TCP 启动一个新的连接或拥塞结束后,将拥塞窗口的大小设置为一个数据段的大小,然后每收到一个确认段,TCP 就将拥塞窗口的大小增加一个数据段;在拥塞发生并恢复后,通过慢启动机制使得拥塞窗口的大小达到了拥塞之前的一半,TCP 就启动一个拥塞避免机制以减缓窗口扩大的速度
4) 傻窗口症状:极端的情况,接收方的应用程序一直没有处理接收到的数据,这样接收方的TCP 缓冲区都被接收到的数据占满,接收窗口的大小减小为0,TCP 将这个窗口大小通知给发送方TCP,这时整个数据传输就停下来了
Flags
可以在点击以太网报文结构查看各协议层协议首部字段及其含义
TCP协议首部格式

上图标注的红圈里有6个标志位,每个标志位占用一个比特
URG
“紧急指针有效标识。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)
”
ACK
“确认序号有效标识。只有当ACK=1时确认号字段才有效。当ACK=0时,确认号无效
”
PSH
“标识接收方应该尽快将这个报文段交给应用层。接收到PSH = 1的TCP报文段,应尽快的交付接收应用进程,而不再等待整个缓存都填满了后再向上交付
”
RST
“重建连接标识。当RST=1时,表明TCP连接中出现严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立连接
”
SYN
“同步序号标识,用来发起一个连接。SYN=1表示这是一个连接请求或连接接受请求
”
FIN
“发端完成发送任务标识。用来释放一个连接。FIN=1表明此报文段的发送端的数据已经发送完毕,并要求释放连接
”
RST
RST
“Reset,复位标志,用于非正常地关闭连接。它是TCP协议首部里的一个标志位。发送RST包关闭连接时,直接丢弃缓冲区的包发送RST包(这个和发FIN包不同)。而接收端收到RST包后,也不必发送ACK包来确认
”
如何触发
正常地关闭连接用FIN标志位,但FIN标志位不能用来处理异常情况
举例
客户端和服务器TCP连接正常,突然服务器掉电重启,与客户端的TCP连接状态由于掉电而完全消失。之后,客户端发给服务器任何消息,都会触发服务器发RST作为回应。服务器之所以发RST,是因为连接不存在,通过Reset状态位,间接告诉客户端异常情况的存在。如果Reset顺利到达客户端,客户端意识到异常发生了,会立马释放该TCP连接所占用的内存资源(状态、数据)、以及端口号,且不会回复ACK
连接未监听的端口
连接一个未监听的端口,则被连接方会发送一个RST。也就是说主机传输层TCP程序接收到一个SYN包,而这个SYN包目的端口并没有socket监听,那么主机的协议栈会直接回复一个RST
目的主机或网络路径中的防火墙拦截
如果目的主机或者网络路径中显式的设置了对数据包的拦截

使用iptables对主机的防火墙添加了一条规则,对于目的端口是60000的TCP报文,丢弃并回复RST
socket接收缓冲区Recv-Q中的数据未完全被应用程序读取时关闭该socket
接收到的数据缓存在缓冲区Recv-Q,它们等待被上层应用取走,如果缓冲区Recv-Q有数据未被应用取走,而此时调用了socket.close()方法强行关闭TCP连接,那么TCP协议程序发送的就不是FIN,而是RST
向已关闭的socket发送数据
顾名思义,主机传输层TCP协议程序接收到一条TCP数据报,而目的端口并没有socket监听,那么主机的协议栈会直接回复一个RST
向已关闭的连接发送FIN
主机传输层TCP协议程序接收到一条FIN,而目的端口并没有socket监听,那么主机的协议栈会直接回复一个RST
向已经消逝的连接中发送数据
和上面的举例相同
请求超时后收到回复
主机创建socket,设置SO_RCVTIMEOUT选项为100ms,向对端发送SYN,过了100ms后才收到ACK+SYN,那么主机的协议栈会直接回复一个RST
SO_LINGER
socket设置SO_LINGER选项,socket调用close()函数时,会直接丢弃缓冲区Send_Q未发完的数据,并发送RST
Linux下启用TIME_WAIT快速回收
修改/etc/sysctl.conf中内核参数: net.ipv4.tcp_tw_recycle = 1 当收到的SYN包的timestamp比上次的小时,就会发RST
TCP socket在任何状态下,只要收到RST包,即可进入CLOSED初始状态,不会有任何回应。至于是否通知上层应用,要根据应用程序是阻塞模式还是非阻塞模式:
阻塞模型下,内核无法主动通知应用层出错,只有应用层主动调用read()或者write()这样的IO系统调用时,内核才会利用出错来通知应用层对端RST
非阻塞模型下,select或者epoll会返回sockfd可读,应用层对其进行读取时,read()会报错RST

浙公网安备 33010602011771号