我正在用 C 编写一个 TCP 服务器,发现一旦监听 fd 出现“太多打开的文件”错误,就会发生一些不寻常的事情。 accept
调用不再阻塞并始终返回 -1。
我也试过关闭监听的fd,重新打开,重新绑定(bind),但是好像没有用。
我的问题是为什么 accept
在这种情况下一直返回 -1,我应该怎么做才能停止它 并使服务器能够在任何旧客户端关闭后接受新连接? (当某些连接关闭时,套接字当然能够再次正确地接受
)
======更新:澄清======
问题的发生只是因为活跃客户端的数量超过了打开的fds的限制,所以我在示例代码中没有关闭
任何接受的fd,只是为了让它重现更快。
我在每次accept
返回输出时添加时间戳并将connect
频率减慢到每2秒一次,然后我发现实际上“Too many open文件”错误发生在最近一次成功 accept
之后。所以我认为那是因为当达到maxium fds时,每次调用accept
都会立即返回,返回值为-1。 (我认为 accept
仍然会阻塞,但在下一个传入的 connect
时返回 -1。在这种情况下 accept
的行为是我自己的理论,不是来自手册页。如果有误,请告诉我)。
所以对于我的第二个问题,为了让它停止,我认为这是一个在任何连接关闭
d之前停止调用accept
的解决方案。
同时更新示例代码。感谢您的帮助。
======示例代码======
我是这样测试的。首先将 ulimit -n
设置为一个较低的值(如 16),然后运行从以下 C 源代码编译的服务器程序;然后使用 Python 脚本创建多个连接
/* TCP server; bind :5555 */
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 1024
#define PORT 5555
void error(char const* msg)
{
perror(msg);
exit(1);
}
int listen_port(int port)
{
int parentfd; /* parent socket */
struct sockaddr_in serveraddr; /* server's addr */
int optval; /* flag value for setsockopt */
parentfd = socket(AF_INET, SOCK_STREAM, 0);
if (parentfd < 0) {
error("ERROR opening socket");
}
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int));
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
error("ERROR on binding");
}
if (listen(parentfd, 5) < 0) {
error("ERROR on listen");
}
printf("Listen :%d\n", port);
return parentfd;
}
int main(int argc, char **argv)
{
int parentfd; /* parent socket */
int childfd; /* child socket */
int clientlen; /* byte size of client's address */
struct sockaddr_in clientaddr; /* client addr */
int accept_count; /* times of accept called */
accept_count = 0;
parentfd = listen_port(PORT);
clientlen = sizeof(clientaddr);
while (1) {
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, (socklen_t*) &clientlen);
printf("accept returns ; count=%d ; time=%u ; fd=%d\n", accept_count++, (unsigned) time(NULL), childfd);
if (childfd < 0) {
perror("error on accept");
/* the following 2 lines try to close the listening fd and re-open it */
// close(parentfd);
// parentfd = listen_port(PORT);
// the following line let the program exit at the first error
error("--- error on accept");
}
}
}
创建连接的Python程序
import time
import socket
def connect(host, port):
s = socket.socket()
s.connect((host, port))
return s
if __name__ == '__main__':
socks = []
try:
try:
for i in xrange(100):
socks.append(connect('127.0.0.1', 5555))
print ('connect count: ' + str(i))
time.sleep(2)
except IOError as e:
print ('error: ' + str(e))
print ('stop')
while True:
time.sleep(10)
except KeyboardInterrupt:
for s in socks:
s.close()
最佳答案
why accept keeps returning -1 in this situation
因为您已经用完了文件描述符,就像错误消息所说的那样。
what am I supposed to do to stop it and make the server be able to accept new connections after any old clients closed?
关闭客户端。 问题 不是accept()
返回-1,而是您没有在完成处理后关闭已接受的套接字。
关闭监听套接字不是解决方案。这只是另一个问题。
编辑“完成他们”是指以下几件事之一:
- 他们已经完成了对你的处理,这由
recv()
返回零表示。 - 你已经完成了他们,例如发送最终回复后。
- 除了 EAGAIN/EWOULDBLOCK 之外,您在向他们发送或从他们那里接收错误时。
- 当您遇到一些其他内部 fatal error 阻止您进一步处理该客户端时,例如收到无法解析的请求,或其他一些使连接或 session 或整个客户端无效的致命应用程序错误.
在所有这些情况下,您应该关闭已接受的套接字。
关于c - TCP 服务器 - 从 "Too many open files"恢复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36784191/