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) {

测试过程 :

- 在 Linux 平台上测试能如愿达到客户的要求, 发起 tcp 连接断开的一端没有进行挥手, 而直接发送 RST
- 初步判断 sylixos 所用的 tcp 协议有 bug, 并找到了导致不能直接触发 RST 的代码段( 见代码段2 )
- 寻找 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.