调用 close 关闭 tcp 链接之后发送 RST 不是 FIN 的问题

调用 close 关闭 tcp 链接之后发送 RST 不是 FIN 的问题

Q:在设置 linger 等于 0 之后, 调用 close 关闭 socket, tcp 协议会发送 RST, 并不走四次挥手的流程, 直接断开 tcp 两端的链接, 并且不进入TIME_WAIT 状态等待, 以降低资源的消耗(其实这种行为在网络条件较差的情况是存在一些弊端的, 降低了 tcp 协议的可靠性) .
而实际情况下, 当 socket 队列中有消息的时候才会发送 RST, linger 的设置并未如愿生效
用例代码片段( 代码段1 ) :
        connfd = accept(sockfd, (struct sockaddr *)&cli, &len);
        if (connfd < 0) {
            printf("server accept failed...\n");
            goto __error_handle_0;
        }
        else {
            printf("server accept the client...\n");
        }
        // 这里就是设置linger等于0的地方    
        struct linger so_linger;
        so_linger.l_onoff=1;
        so_linger.l_linger=0;
        if(setsockopt(connfd,SOL_SOCKET,SO_LINGER,&so_linger,sizeof(so_linger)) != 0) {
        goto __error_handle_1;
        }

测试过程 :
  1. 在 Linux 平台上测试能如愿达到客户的要求, 发起 tcp 连接断开的一端没有进行挥手, 而直接发送 RST
  2. 初步判断 sylixos 所用的 tcp 协议有 bug, 并找到了导致不能直接触发 RST 的代码段( 见代码段2 )
  3. 寻找 reference, 参考 POSIX 标准定义( 定义见下方REF )
在第三步发现, sylixos 中所用的 tcp 协议在此处处理并没有 bug, 而是严格符合 POSIX 的规范的, 但是由于客户存在要求, 以及广泛使用的Linux 中的 tcp 协议执行方式和 sylixos 中的不一样, 故留下记录

问题的解决方法 :
在 base/libsylixos/SylixOS/net/lwip/src/api/api_msg.c 文件中存在以下代码( 代码段2 )

/* check linger possibilities before calling tcp_close */
    err = ERR_OK;
    /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
    /* SylixOS Fixed listen conn do not need check linger */
    if ((conn->linger >= 0) &&
        (conn->pcb.tcp->state != LISTEN) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
      if ((conn->linger == 0)) {
        /* data left but linger prevents waiting */
        tcp_abort(tpcb);
        tpcb = NULL;
      } else if (conn->linger > 0) {
        /* data left and linger says we should wait */
        if (netconn_is_nonblocking(conn)) {
          /* data left on a nonblocking netconn -> cannot linger */
          err = ERR_WOULDBLOCK;
        } else if ((s32_t)(sys_time_diff(sys_now(), conn->current_msg->msg.sd.time_started)) >=
                   (conn->linger * 1000)) { /* SylixOS Fixed time difference overflow */
          /* data left but linger timeout has expired (this happens on further
             calls to this function through poll_tcp */
          tcp_abort(tpcb);
          tpcb = NULL;
        } else {
          /* data left -> need to wait for ACK after successful close */
          linger_wait_required = 1;
        }
      }
    }
&& (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)  条件移动合并到 conn->linger > 0 条件中即可

REF :
对 SO_LINGER 选项的描述如下
The SO_LINGER option controls the action of the interface when unsent messages are queued on a socket and a close() is performed. The details of this option are protocol-specific. If SO_LINGER is enabled, the system shall block the calling thread during close() until it can transmit the data or until the end of the interval indicated by the l_linger member, whichever comes first. If SO_LINGER is not specified, and close() is issued, the system handles the call in a way that allows the calling thread to continue as quickly as possible. The default value for SO_LINGER is zero, or off, for the l_onoff element of the option value and zero seconds for the linger time specified by the l_linger element.



    • Related Articles

    • TCP/UDP 通信中 server 端异常关闭后无法再次连接

      Q:在 TCP/UDP 通信中,服务器和客户端正常通信时若手动异常关闭 server 端进程,再次运行开启 server 端程序会在 bind 函数位置报错,造成无法连接? 尝试在 bind 函数前添加 setsockopt 函数增加复用功能,重用本地地址,代码如下  iRet = setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &iRet, sizeof(int));     if(iRet < 0){         ...
    • SylixOS TCP 数据段发送流程简述

      Q:SylixOS TCP 数据段发送流程简述 1、在 LwIP 协议栈中使用 TCP 控制块结构 struct tcp_pcb 管理 TCP 连接,使用 TCP 段结构 struct tcp_seg 描述 TCP 段。在 TCP 控制块中有两个队列 unsent 与 unacked 表示所有未发送的 TCP 数据段和发送了还未收到确认的 TCP 数据段。如下图所示。 2、TCP 连接发送数据由应用层发起,数据段构建后首先被缓存在 TCP 控制块的 unsent ...
    • 打印指定线程的调用栈

      Q:线程运行不正常,发现阻塞了,需要打印其调用连。 SylixOS 支持通过命令可以获取指定线程的调用链。 通过 shell 交互输入:kill -n 47 线程 id
    • SylixOS 动态库更新 version 不一致的问题

      Q:SylixOS 动态库更新 version 不一致的问题 首先 SylixOS 是允许多个进程对共享库文件进行代码段共享的(默认是共享打开的,可以使用 dlconfig share dis 命令进行关闭),代码段共享会出现一个问题:当 A  进程 使用一个 share.so 共享库的时候,B 进程也使用 该 share.so 的共享库,share.so 共享库因为 B 的原因需要修改,修改后,更新share.so文件,然后重启 B 进程,一般来说会出现错误,错误如下: ...
    • SylixOS TCP 数据段接收流程简述

      Q:SylixOS TCP 数据段接收流程简述 1、数据包最初由网卡驱动中断接收,通过调用 tcpip_input() 送入 LwIP 协议栈; 2、在 tcpip_inpkt() 中将 ip_input() 通过 mbox (本质为消息队列) 投递至线程 “t_netproto”,并在线程中执行 ip_input(); 3、在 ip_input() 中针对 IPv4 报文调用 ip4_input() 进行处理。针对 TCP 报文,tcp_input() 处理了基本的协议规则; ...