C语言Socket疑问:服务器关闭套接字后客户端仍可发送两次数据
关于TCP客户端在服务器关闭Socket后仍能发送数据包的问题解答
首先,这个现象其实是TCP协议的正常行为,咱们来一步步拆解原因和解决办法:
为什么服务器关闭Socket后,客户端还能发送两个数据包?
TCP连接的关闭是四次握手的过程:
- 服务器调用
close()关闭Socket后,会向客户端发送一个FIN报文,表明服务器不再发送数据。 - 客户端收到FIN后,TCP连接进入
CLOSE_WAIT状态——此时客户端的TCP栈认为连接还可以发送数据(因为客户端还没有主动关闭连接,也就是还没发送自己的FIN),所以第一次send()会成功,数据会被发送到服务器。 - 服务器收到客户端的这个数据包后,因为Socket已经关闭,会回复一个RST报文给客户端,告诉客户端连接已经被重置。
- 当客户端的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
相关产品推荐
相关产品推荐

