【发布时间】:2017-08-23 03:24:02
【问题描述】:
我的 TCP 服务器中有一个 epoll 事件循环来处理客户端连接并从客户端读取数据。
while(1) {
int n, i;
n = epoll_wait(efd, events, 64, -1); // This is blocking. It waits till new events arrive
for(i = 0; i < n; i++) {
if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
/* An error has occured on this fd, or the socket is not
ready for reading */
dzlog_error("epoll error: %s", strerror(errno));
close(events[i].data.fd);
continue;
} else if(sock == events[i].data.fd) { // Event on the server socket. Accept client connection
while(1) {
if((cli = accept(sock, (struct sockaddr *)&their_addr, &addr_size)) == -1) {
if((errno == EAGAIN) || (errno == EWOULDBLOCK)) { // We have processed all incoming connections
break;
} else {
dzlog_error("accept: %s", strerror(errno));
break;
}
}
dzlog_info("Client connected: Identifier - %d", cli);
s = fcntl(cli, F_SETFL, O_NONBLOCK); // Make client socket non-blocking
if(s == -1) {
dzlog_error("Client no block: %s", strerror(errno));
close(cli);
break;
}
event.data.fd = cli;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl (efd, EPOLL_CTL_ADD, cli, &event); // Add the client socket to the list of file descriptors to poll
if(s == -1) {
dzlog_error("epoll_ctl: %s", strerror(errno));
close(cli);
break;
}
}
continue;
} else {
readClientData(events[i].data.fd);
}
}
}
当有数据要从客户端套接字读取时,会调用readClientData 函数。让我们假设在该函数内部,我们调用了一个从表中获取一些数据的数据库。如果由于某种原因,对数据库的调用挂起或花费的时间比预期的要长,其他等待连接或发送数据的客户端也将被阻塞。
例如考虑以下场景:
- 客户端 1 连接到服务器
- 客户端 2 连接到服务器
- 客户端 1 向服务器发送数据(这将导致调用
readClientData函数来处理数据) -
readClientData函数调用数据库并等待响应。 (等待 10 秒或可能无限期挂起) - 客户端 2 发送数据。无法处理此数据,因为服务器仍在等待客户端 1 的
readClientData完成 - 新的客户端 3 尝试连接,但必须等待其连接被接受,因为服务器仍在处理来自客户端 1 的数据
有没有办法解决这个问题?
谢谢
【问题讨论】:
-
为什么不让服务器在成功连接时分叉进程?
-
@Chirality 这对性能来说太可怕了,这就是我选择均匀驱动方法的原因
-
我应该澄清一下。你不能 fork
readClientData函数,以便服务器(父进程)继续接受和处理这种事件驱动方法中的连接吗?我不确定您是否可以等待它执行该功能。 -
@Chirality 这和每个连接都有一个子进程不一样吗?如果我有 50k 个客户端都发送数据,我将有 50k 个子进程
-
我建议学习 node.js 的工作原理——即使你没有在其中编写应用程序。
标签: c sockets concurrency server epoll