TCP状态变迁
CLOSE: socket的初始状态,没有进行任何操作(connect, listen)之前的状态
LISTEN: 可以接受SYN的状态,服务器等待连接
SYN_RECEIVED: 一个连接请求已经到达,等待确认
SYN_SENT: 发出连接请求,等待确认
ESTABLISED: 连接建立成功,可以进行数据通信
CLOSE_WAIT: 对方已关闭连接,等待本地用户关闭
FIN_WAIT_1: 应用程序关闭连接
FIN_WAIT_2: 对方已经知道本端不会再发送数据
CLOSING: 对待对方的连接终止请求确认
TIME_WAIT: 双方都尝试关闭连接,等待一段时间,以防止ACK丢失
LAST_ACK: 等待对方确认关闭连接
同步状态:ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, CLOSE_WAIT, CLOSING, LAST_ACK, TIME_WAIT
非同步状态:LISTEN,SYN_RECEIVED, SYN_SENT
从LISTEN到SYN_SENT的变迁是正确的,但伯克利的TCP软件并不支持它。这里也没有画出。
从程序开发者的角度来看RST的产生和RST的处理
RST的产生
- 向不存在的端口发起连接
- 一些延迟的包导致连接无法正常建立,例如一个旧的SYN副本
- 程序异常终止(依赖于系统,有的会发送FIN, 有的发送RST)
- 设置了SO_LINGER选项支持异常关闭,并调用close
- 已关闭的端口(调用close)收到数据
RST的处理
内核对rst的处理由下面这个函数完成
/* When we get a reset we do this. */
void
tcp_reset
(
struct
sock
*
sk
)
{
/* We want the right error as BSD sees it (and indeed as we do). */
switch
(
sk
->sk_state) {
case
TCP_SYN_SENT:
sk
->sk_err =
ECONNREFUSED
;
break
;
case
TCP_CLOSE_WAIT:
sk
->sk_err =
EPIPE
;
break
;
case
TCP_CLOSE:
return
;
default
:
sk
->sk_err =
ECONNRESET
;
}
/* This barrier is coupled with smp_rmb() in tcp_poll() */
smp_wmb();
if
(!sock_flag(
sk
, SOCK_DEAD))
sk
->sk_error_report(
sk
);
tcp_done(
sk
);
}
对rst的处理结果最终要反应给上层应用。通过设置errno告诉上层TCP的当前状态。对于与tcp_reset函数
我们可以得到如下的处理流程图
即,
- 如果在建立连接的过程中收到RST,上层得到的错误就是ECONNREFUSED,一般就是connect返回失败,errno
的值被设置为ECONNREFUSED。
- 如果对方已经关闭连接(调用了close, 本端处于CLOSE_WAIT状态),继续调用send发送数据,就会收到RST,这种情况
下设置错误号为EPIPE。
- 其它情况下,收到RST,都设置错误号为ECONNRESET
掉用close函数的TCP会进入CLOSE状态,调用shutdown可能进入FIN_WAIT_2状态。