套接字和文件描述符的重用(或缺乏)

4
在下面的服务器代码中调用socket后,我遇到了“打开文件过多”的错误。这段代码会反复调用,并且仅在server_SD获取值1022后才会出现此错误。因此,我认为我已经达到了由“ulimit -n”规定的1024的限制。但我不明白的是,我关闭了Socket,这应使fd可重用,但似乎并没有发生这种情况。
注意:使用Linux,并且客户端也已关闭。否则,我不是root用户,因此无法移动限制。我一次应最多打开约20个套接字。在程序的整个生命周期中,我希望打开和关闭近1000000个套接字(因此需要非常强大的重用)。
  server_SD = socket (AF_INET, SOCK_STREAM, 0);  
  bind (server_SD, (struct sockaddr *) &server_address, server_len)  
  listen (server_SD,1)  
  client_SD = accept (server_SD, (struct sockaddr *)&client_address, &client_len)  
  // read, write etc...   
  shutdown (server_SD, 2);  
  close (server_SD)

有人知道如何保证闭合性和可重用性吗?

谢谢。


客户端套接字怎么样? - anon
代码看起来还行。你还打开了哪些文件描述符?你打开了 1022 个文件描述符。在调试过程中,请检查 socket 返回的值,找出它们为什么会增加的原因。也就是说,要检查调用 open、fopen、pipe、popen 等函数的返回值,在两个 socket 调用之间进行检查。 - William Pursell
3
你是否实际上在任何地方关闭了 client_SD? - MarkR
是的,MarkR,客户端也关闭了。 - Nigel
是的,那就是问题所在... 在关闭服务器之前需要先关闭客户端(与我上面的代码顺序相反!)感谢所有提供建议的人! - Nigel
显示剩余8条评论
6个回答

3

使用--track-fds=yes选项在valgrind下运行您的程序:

valgrind --track-fds=yes myserver

如果你的程序使用了包装器或者将自己放在后台运行,你可能还需要添加--trace-children=yes选项。

如果程序没有自动退出,在它累积了一些泄漏文件描述符之后,请使用"kill pid"(不是-9)中断或杀死该进程。退出时,valgrind会显示仍然打开的文件描述符以及它们创建时对应的堆栈跟踪。

strace下运行程序以记录所有系统调用也可能很有帮助。另一个有用的命令是/usr/sbin/lsof -p pid,可以显示所有当前使用的文件描述符以及它们的用途。


2
从你的描述中看起来,你为每个accept(2)打开了服务器套接字。这是不必要的。只需创建一次服务器套接字,绑定(bind(2))它,监听(listen(2)),然后在循环中调用accept(2)(或者更好的方法是将它传递给poll(2))。
另外,对监听套接字使用shutdown(2)是完全没有意义的,它仅适用于已连接的套接字。

正确,我正在为每个接受打开服务器套接字。然而,以这种方式重新组织我的程序是不可能的,我需要保持端口和服务器之间严格的1:1耦合。此外,这并不能解释为什么fd无法被释放。 - Nigel
1
你每次绑定服务器套接字时端口都不同吗? - Nikolai Fetissov

0

也许你的问题是没有指定 SO_REUSEADDR 标志?

来自 socket 手册:

SO_REUSEADDR 表示在验证 bind(2) 调用中提供的地址时使用的规则应允许重用本地地址。对于 PF_INET 套接字,这意味着套接字可以绑定,但当地址绑定到活动侦听套接字时除外。当侦听套接字绑定到 INADDR_ANY 且特定端口时,则无法将其绑定到任何本地地址的此端口。


是的,我正在使用“SO_REUSEADDR”: int yes = 1; setsockopt(server_SD,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)); - Nigel

0
你是否在使用fork()?如果是的话,你的子进程可能继承了已打开的文件描述符。 如果是这种情况,你应该让子进程关闭任何不属于它的文件描述符。

不要使用fork。我的客户端进程是通过系统调用生成的,并且不会从服务器/父进程继承任何内容。 - Nigel

0
这看起来像是你可能有一个“TIME_WAIT”问题。如果我没记错,TIME_WAIT是TCP套接字可以处于的状态之一,当双方关闭连接时它被输入,但系统会保留该套接字一段时间,以避免延迟的消息被接受为后续连接的正确负载。
你应该看看this(第99页底部和第100页顶部)。也许还要看看other question

1
我已经使用了以下代码: setsockopt(server_SD, SOL_SOCKET, SO_LINGER, &l, sizeof (l)); 其中,l的值为0。因此,套接字不应该停留。此外,通过查看netstat,我发现连接很快就会消失,但是“ls /proc/<pid>/fd”告诉我文件描述符没有被释放。 - Nigel
1
如果拥有TIME_WAIT套接字的进程已经关闭了它们,那么它们不应该消耗文件描述符。 - MarkR
使用SO_REUSEADDR而不是SO_LINGER,除非您确切地了解两个选项的作用。 - Kristof Provost
我相信我可以,你是说它们不应该同时使用吗? - Nigel

-1

关闭服务器之前需要关闭客户端(与我上面的代码顺序相反!)
感谢所有提供建议的人!


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