在TCP协议中,rst段标识复位,用来异常的关闭连接。在TCP的设计中它是不可或缺的,发送rst段关闭连接时,不必等缓冲区的数据都发送出去,直接丢弃缓冲区中的数据。而接收端收到rst段后,也不必发送ack来确认。 监听的一端为服务端,主动连接的一端为客户端。

tcp是全双工的数据通信,也就是说任意一端的连接都可以主动的向对端发送数据。

情况1 目标端口未监听

连接一个未监听的端口,则被连接方会发送一个rst段。

但并不是所有连接未监听端口的情况下都会返回rst段,感觉与具体的网络实现有关。

情况2 目的主机或者网络路径中防火墙拦截

如果目的主机或者网络路径中显式的设置了对数据包的拦截。

当连接目标主机的60000端口时,会产生rst。

情况3 socket接收缓冲取Recv-Q中的数据未完全被应用程序读取时关闭该socket

python端的socket Recv-Q中有nc发送过来的10个字节未被应用read消费掉。

此时,python调用cli的close方法,则会产生rst段。

情况4 向已关闭的socket发送数据

socket调用close,表示本方既没有发送的需求,也没有接收的需求。不同于shutdown

sock connect到60000端口,然后服务端cli调用close,关闭连接。

当前服务端连接处于FIN_WAIT2状态。

客户端通过sock.send向已关闭的连接发送数据,则会产生rst。

情况5 向已关闭的连接发送FIN

cli.close关闭服务端连接,当前服务端连接处于FIN_WAIT2状态,等待对端的FIN段。

服务端的连接在FIN_WAIT2状态超时,当前服务端的连接实际上已经消逝。

此时,客户端调用sock.close()关闭连接,则服务端产生rst。

情况6 向已经消逝的连接中发送数据 消逝连接指的是,当前这个连接状态操作系统已经不再维护,其数据结构内核已经注销。

比如情况5中socket FIN_WAIT2超时后,其实该连接已经不存在。

再比如半打开(Half Open)连接的对端,由于某种原因已经不存在。

最近在工作中遇到一个由于服务端accept()调用过慢导致的已连接队列满,而客户端是半开打(Half Open)连接的情况下产生rst。

注:为了方便模拟已连接队列满的情况,将listen socket的backlog参数设置为1。

服务端监听6000端口,并不进行accept。

通过ss命令,可以看到端口60000的Send-Q为1,代表当前listen socket的已连接队列为1。

通过nc进行连接60000端口,可以看到两个连接都正常建立起来,其中服务端两个连接状态处于ESTABLISHD,但是服务端的PID为-,表示当前socket并没有和进程绑定。

通过ss命令,可以看到,60000端口的Listen socket的Recv-Q的值为2,该值表示已连接队列中有2个连接没有被应用accept取走。

第3个客户端使用nc进行连接,通过netstat查看网络状态,可以看到当前客户端已经完成了握手,而服务端因为已连接队列满,而处于SYN_RECV状态。

SYN_RECV状态的连接存在于listen socket的半连接队列中。

服务端SYN_RECV状态的连接超时以后消逝,而当前第3个nc客户端的连接依然处于ESTABLISHED状态,实际上是一个半打开(Half Open)连接。

对半打开连接进行send操作,则会产生rst。

rst与 broken pipe 对已关闭的管道进行操作会产生SIGPIPE信号。

网络编程中,对已经收到rst的连接进行io操作会产生SIGPIPE信号。

nc作为服务端监听60000端口,python sock进行连接。

nc服务端进程退出。

向已关闭的连接调用send将产生rst。

向已接收到rst的连接进行send将产生SIGPIPE信号。

https://zhuanlan.zhihu.com/p/30791159