客户端断开连接后,我是否需要在服务器端关闭socket?

8

让我来简化一下我正在处理的代码:

int fd = socket(...);
int client = accept(fd, ...);
while (true) {
    int bytes_read = recv(client, ...);
    if (bytes_read == 0) {
        break;
    }
};

此时我已经知道客户端已断开连接(recv返回0)。但我是否仍需要调用

close(client);

?


我认为你需要这样做,因为如果你不关闭文件描述符,那么会导致内存泄漏。 - kaldoran
2
如果您不调用close(),并且不断创建新的套接字(例如通过accept()),那么最终您的进程套接字表将填满剩余的套接字,然后您的进程将无法再创建更多套接字,因此accept()、socket()等都将失败,并显示errno=EMFILE,然后您的程序将不能正常工作:( - Jeremy Friesner
3个回答

13

是的。

当你通过recv()函数接收到0字节时,这就是内核告诉你远程主机关闭了他们的连接中的发送端。他们仍然可能保持连接打开状态,可以接收你发送的更多数据(但无法回复)。

当你在TCP套接字上调用close()函数时,你正在做两件事:

  1. 关闭套接字的本地一侧,让远程主机知道你也完成了传输。如果不这样做,你可能会让客户端挂起,等待你发送更多的数据。
  2. 关闭文件描述符。这将释放进程打开文件表中的条目。如果不这样做,你将浪费资源,并可能发现自己内存不足或可用文件描述符不足。大多数操作系统都有进程可以同时打开的文件描述符(或句柄)数量上限。

广告1:客户端(远程主机)无需通知(它已经死了),但连接表中的条目需要被释放(它可能会在某段时间内保持在等待状态)。这在您的(2)中自动处理:通过close(fd)进行处理。 - wildplasser
@wildplasser 关于1,我认为你误解了我的意思。客户端可能并没有“死亡”——他只调用了shutdown(fd, SHUT_WR),仍在等待服务器发送更多数据。在这种情况下,他确实需要被告知。 - Jonathon Reinhart
没错,它可能是半关闭的。但我完全同意你的观点:调用close(fd)是正确的方法。 - wildplasser
@Jonathon,收到EOF时调用shutdown是无意义的,因为另一端已经关闭了连接或者至少关闭了其发送连接的一部分。调用shutdown()会以与close()相同的方式关闭连接的另一半。关于“需要通知”,这也会发生在close()中,不需要调用shutdown()。 - Meixner
谢谢@Meixner,但是这条评论为什么是针对我的?我很清楚close()隐含了shutdown()。我从来没有建议调用shutdown是必须的。 - Jonathon Reinhart

1

您需要关闭描述符以释放绑定到其上的资源。


1
答案是肯定的。原因在于accept()分配内存,而close()释放该内存。
想象一种理论上的情况,您的代码处于循环中,并且每次调用accept()时,它会分配1GB的RAM(假装它是一个非常低效的套接字库:-))。每当您调用close()时,它就会释放1GB的RAM。如果您从未调用close(),那么在耗尽内存之前,accept()会发生多少次循环?
当然,accept()只分配了一小块内存,但最终,除非您采取某些措施,否则您将耗尽内存。

2
内存本身并不是真正的问题。会咬你的是打开文件描述符的限制。我认为你需要重新措辞你的答案,因为这句话是假设性的,不清楚。 “每次调用accept()时,它会分配1GB的RAM。” - Jonathon Reinhart

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