TCP/IP学习笔记(八)复位报文段

TCP报文首部中存在一个RST位,如果该位被置1则表示这是个复位报文段。当一个报文段从一端发往一个不存在或者处于异常状态的另一端时,就会以一个复位报文段应答发送端,告知发送端连接出现错误,应当被关闭

有三种连接情况可能会产生复位报文段

  • 尝试连接到一个不存在的<ip,port>
  • 主动关闭的一方的套接字设置了SO_LINGER选项,并且超时时间为0
  • 另一端异常崩溃导致连接处于半关闭状态,此时正常的一端向已关闭的一端发送报文段

向不存在的端口发送连接请求

如果客户端尝试连接到port端口上而这个端口根本就没有服务器监听,那么当客户端发送三次握手的第一个SYN报文段时另一端会以复位报文段回应

可以在终端使用telnet ip port命令向

➜  ~ telnet localhost 9999
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
➜  ~ 

主动关闭的一方设置了SO_LINGER选项

SO_LINGER选项用于对连接关闭提供更多的控制,它影响的是close函数的行为。需要配合struct linger结构使用

struct linger
{
    int l_onoff;    // 开关选项
    int l_linger;   // 超时时间
};

使用方法为

struct linger so_linger;
so_linger.l_onoff = m;
so_linger.l_linger = n;
::setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));

根据上述设置的l_oneoff和l_linger不同,SO_LINGER有三种不同的行为

  • l_onoff = 0,表示关闭SO_LINGER选项,调用close函数时采用默认行为,即四次挥手,close立即返回
  • l_onoff != 0,l_linger = 0,表示开启SO_LINGER选项,调用close函数时不进行四次挥手而直接发送复位报文段给对端,close立即返回
  • l_onoff != 0,l_linger = 0,表示开启SO_LINGER选项,close函数变为阻塞函数,调用时会先将发送缓冲区中的数据全部发送出去并收到对端的ACK应答报文段(或者到达超时时间)再返回close函数。并且这种情况下不管套接字是否设置非阻塞close函数都会阻塞

通过wireshark观察报文段发送情况,客户端服务器的行为如下

  • 建立连接,客户端设置SO_LINGER选项并设置l_onoff != 0,l_linger = 0(上述第二种情况)
  • 0.5秒后客户端调用close函数终止连接
  • 服务器收到复位报文段后(read/recv返回-1)输出错误信息,并调用close函数终止连接

服务器输出错误信息

server ./server
Connection reset by peer

复位报文段不需要对端应答,因为RST代表连接出现错误,双方只要各自关闭就可以了

半关闭连接情况下发送数据

在介绍保活定时器(KEEPALIVE)时提到过,如果通信双方的一端突然崩溃,会导致连接处于半关闭状态,也就是一端已经关闭,而另一端仍处于打开状态。在这种情况下,如果正常的一端向已关闭的一端发送数据,由于对端已经重启,丢失了所有连接信息,会返回一个复位报文段

借用<TCP/IP详解>中的例子,在连接正常的情况下断开服务器以太网电缆并重启,随后客户端发送数据

观察tcpdump结果

最终,服务器由于无法识别发来的连接,发送给客户端复位报文段以告知对方关闭连接

猜你喜欢

转载自blog.csdn.net/sinat_35261315/article/details/79416752
今日推荐