如何在C中完全销毁一个套接字连接

33

我在Linux上使用socket制作了一个聊天客户端,我希望完全断开连接。以下是相关部分代码:

int sock, connected, bytes_recieved , true = 1, pid;  
char send_data [1024] , recv_data[1024];     
struct sockaddr_in server_addr,client_addr;    
int sin_size;
label:
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
    perror("Socket");
    exit(1);
}
if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int)) == -1)
{
    perror("Setsockopt");
    exit(1);
}
server_addr.sin_family = AF_INET;         
server_addr.sin_port = htons(3128);     
server_addr.sin_addr.s_addr = INADDR_ANY; 
bzero(&(server_addr.sin_zero),8); 
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))== -1)
{
    perror("Unable to bind");
    exit(1);
}
if (listen(sock, 5) == -1)
{
    perror("Listen");
    exit(1);
}
printf("\nTCPServer Waiting for client on port 3128");
fflush(stdout);
connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size);
//necessary code
close(sock);
goto label;

但是 close(sock) 似乎并没有完全关闭销毁连接,因为在进入“label”后,代码仍然显示错误消息并退出。

Unable to bind: Address already in use

连接没有再次发生。问题可能是什么?谢谢。

编辑:实际上我想要的是,在销毁连接后从头运行脚本时,它应该像全新程序一样运行。我该怎么做?


1
你为什么这样做?你应该在“accept”之前回到循环,而不是重新创建服务器套接字。 - Mat
1
你为什么想要关闭和重新打开监听套接字,而不是循环使用 accept() - dwalter
1
我收到了回复:“您已连接到<ip>,<port>”---这是从哪里来的?你的代码中没有包含那个。请展示相关的代码。 - n. m.
printf("\n 你已连接到 %s , %d)", inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port)); 这段代码应该放在 accept() 下面。 - Harikrishnan
http://serverfault.com/questions/329845/how-to-forcibly-close-a-socket-in-time-wait - Ciro Santilli OurBigBook.com
显示剩余2条评论
6个回答

50
< p> close 调用只是标记TCP套接字关闭了。它不再可被进程使用。 但内核可能仍会在某个时间段内持有一些资源(TIME_WAIT,2MLS等等)。

设置 SO_REUSEADDR 应该解决绑定问题。

因此,在调用 setsockopt 时,请确保 true 的值确实为非零(溢出错误可能会覆盖它):

true = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int))

在你的代码中有一个名为pid的变量。如果你使用 fork(用于启动连接处理进程),那么你应该在不需要它的进程中关闭sock


9

首先是命名问题,我们需要统一命名相同的内容:

服务器端:

传递给 listen()accept() 的套接字,我们称之为 监听 套接字。由 accept() 返回的套接字,我们称之为 已接受 套接字。

客户端:

传递给 connect() 的套接字,我们称之为 连接/已连接 套接字。


关于你的问题:

要终止 accept() 的连接,请通过可选地首先使用 shutdown(),然后使用 close() 关闭 已接受 套接字(即你所说的已连接)。

然后,在调用 accept() 之前循环回去以接受新连接,不要 再次经过 bind()listen()

仅在想要摆脱 accept() 返回后发出的 挂起 connect() 时,才会 关闭和 关闭 监听 套接字。


1
关闭之前不需要关机。关闭意味着关机,除非SD与另一个进程共享,这在这里不是这种情况。而且你永远不需要关闭监听套接字。 - user207421
@user207421:我已相应地修改了我的答案。 - alk

9
连接仍然保持活动状态,因为您忘记关闭已连接的套接字。关闭监听套接字不会自动关闭已连接的套接字。
//necessary code
close(connected);  // <---- add this line
close(sock);
goto label;

我不确定你为什么会遇到EADDRINUSE错误。这段代码在Linux和Mac OS上都能正常工作。


0

正确答案是关于SO_REUSEADDR的。

如果您是监听器,并且接受连接,则必须在 close()之前调用shutdown()(至少在Linux上)。如果您只调用close(),则不会 ACK 客户端正在发送的FIN以关闭连接,客户端可能会陷入CLOSE_WAIT状态。


0

3
我已经尝试过了,但仍然没有成功。连接未能断开。 - Harikrishnan
1
不需要在关闭之前返回。 - user207421

-3

因为当另一方确认连接或经过一定的超时时间后,连接才会真正关闭。这是正常的行为...

编辑:我实际上没有检查过你的代码,但从其他用户的评论中可以看出,你做错了什么...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接