You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言Socket疑问:服务器关闭套接字后客户端仍可发送两次数据

关于TCP客户端在服务器关闭Socket后仍能发送数据包的问题解答

首先,这个现象其实是TCP协议的正常行为,咱们来一步步拆解原因和解决办法:

为什么服务器关闭Socket后,客户端还能发送两个数据包?

TCP连接的关闭是四次握手的过程:

  1. 服务器调用close()关闭Socket后,会向客户端发送一个FIN报文,表明服务器不再发送数据。
  2. 客户端收到FIN后,TCP连接进入CLOSE_WAIT状态——此时客户端的TCP栈认为连接还可以发送数据(因为客户端还没有主动关闭连接,也就是还没发送自己的FIN),所以第一次send()会成功,数据会被发送到服务器。
  3. 服务器收到客户端的这个数据包后,因为Socket已经关闭,会回复一个RST报文给客户端,告诉客户端连接已经被重置。
  4. 当客户端的TCP栈收到RST后,再调用send()就会触发SIGPIPE信号——默认情况下这个信号会直接终止进程,所以你看不到send()返回-1;如果捕获了这个信号但直接退出,同样也看不到返回值。

为什么send/recv没有返回-1?

你注册了SIGPIPE的处理函数exitp(),当触发信号时进程直接退出了,所以根本没机会看到send()返回-1。如果让进程不退出,就能看到send()的错误返回。

解决办法

这里给你几个实用的处理方案:

1. 忽略SIGPIPE信号,让send()返回错误码

把信号处理改成忽略:

signal(SIGPIPE, SIG_IGN);

这样当连接被重置时,send()会返回-1,同时errno会被设置为EPIPE,你可以在代码里检查这个返回值和错误码来处理连接异常:

ssize_t ret = send(skfd, buffer, len, 0);
if (ret == -1) {
    if (errno == EPIPE) {
        fprintf(stderr, "Connection reset by peer\n");
        close(skfd);
        exit(1);
    }
    // 处理其他send错误
}

2. 及时检测连接关闭,主动关闭客户端Socket

当客户端的recv()返回0时,说明服务器已经发送了FIN,连接已经关闭,这时候应该主动调用close()关闭客户端的Socket,避免后续无效的send()操作:

ssize_t recv_len = recv(skfd, buffer, BUFFERSIZE, 0);
if (recv_len == 0) {
    // 服务器已经关闭连接
    fprintf(stderr, "Server closed connection\n");
    close(skfd);
    exit(0);
} else if (recv_len == -1) {
    // 处理recv错误
}

3. 使用MSG_NOSIGNAL标志避免触发SIGPIPE

在调用send()时加上这个标志,也可以避免触发SIGPIPE,直接返回错误:

ssize_t ret = send(skfd, buffer, len, MSG_NOSIGNAL);
if (ret == -1) {
    // 处理错误,包括EPIPE
}

内容的提问来源于stack exchange,提问作者Chongju Mai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:00:56