Linux tcp/ip 源码分析 - shutdown

2019-06-11 15:35:31 浏览数 (1)

之前的文章已经分析了tcp的建立过程以及tcp读和写,下面我们继续看下shutdown方法。

代码语言:javascript复制
// net/socket.c
SYSCALL_DEFINE2(shutdown, int, fd, int, how)
{
  int err, fput_needed;
  struct socket *sock;

  sock = sockfd_lookup_light(fd, &err, &fput_needed);
  if (sock != NULL) {
    ...
    if (!err)
      err = sock->ops->shutdown(sock, how);
    ...
  }
  return err;
}

该方法先根据fd找到对应的socket,再调用sock->ops->shutdown指向的方法继续执行shutdown逻辑。

由第一篇文章我们可以知道,sock->ops->shutdown指向的方法是inet_shutdown。

代码语言:javascript复制
// net/ipv4/af_inet.c
int inet_shutdown(struct socket *sock, int how)
{
  struct sock *sk = sock->sk;
  ...
  switch (sk->sk_state) {
  ...
  default:
    sk->sk_shutdown |= how;
    if (sk->sk_prot->shutdown)
      sk->sk_prot->shutdown(sk, how);
    break;
  ...
  }

  /* Wake up anyone sleeping in poll. */
  sk->sk_state_change(sk);
  ...
  return err;
}
EXPORT_SYMBOL(inet_shutdown);

方法描述

1. 将shutdown类型how标记到sk->sk_shutdown字段。

2. 调用sk->sk_prot->shutdown指向的方法继续执行shutdown逻辑。

3. 调用sk->sk_state_change方法,唤醒那些在监听sock状态变化的阻塞线程。

由第一篇文章可以知道,sk->sk_prot->shutdown指向的方法为tcp_shutdown。

代码语言:javascript复制
// net/ipv4/tcp.c
void tcp_shutdown(struct sock *sk, int how)
{
  ...
  if (!(how & SEND_SHUTDOWN))
    return;

  /* If we've already sent a FIN, or it's a closed state, skip this. */
  if ((1 << sk->sk_state) &
      (TCPF_ESTABLISHED | TCPF_SYN_SENT |
       TCPF_SYN_RECV | TCPF_CLOSE_WAIT)) {
    /* Clear out any half completed packets.  FIN if needed. */
    if (tcp_close_state(sk))
      tcp_send_fin(sk);
  }
}
EXPORT_SYMBOL(tcp_shutdown);

方法描述

1. 如果shutdown类型how中,不包括SEND_SHUTDOWN,即只包括RCV_SHUTDOWN,则不用再执行其他逻辑,直接返回就好。

当我们在调用shutdown方法时,如果只指定RCV_SHUTDOWN,最终结果只是标记sk->sk_shutdown字段,使其值包含RCV_SHUTDOWN,并不会再执行其他tcp逻辑。

2. 检查sk->sk_state字段状态,判断是否已经发送了fin消息,或者是否已经是closed状态,如果是,也不用再继续了。

3. 调用tcp_close_state方法,根据当前sk->sk_state的状态,设置其下一状态,比如,如果当前状态为TCP_ESTABLISHED,则下一状态应该为TCP_FIN_WAIT1,之后该方法返回是否需要发送fin消息。

4. 如果需要,则发送fin消息给对方。

完。

0 人点赞