我尝试使用以下参数复制您的问题:
- 服务器使用 epoll 来管理连接。
- 我建立了 3000 个连接。
- 连接被阻塞。
- 服务器基本上“简化”为仅处理连接并执行非常少的复杂工作。
我无法复制这个问题。这是我的服务器源代码。
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <err.h>
#include <sysexits.h>
#include <string.h>
#include <unistd.h>
struct {
int numfds;
int numevents;
struct epoll_event *events;
} connections = { 0, 0, NULL };
static int create_srv_socket(const char *port) {
int fd = -1;
int rc;
struct addrinfo *ai = NULL, hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
if ((rc = getaddrinfo(NULL, port, &hints, &ai)) != 0)
errx(EX_UNAVAILABLE, "Cannot create socket: %s", gai_strerror(rc));
if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
err(EX_OSERR, "Cannot create socket");
if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
err(EX_OSERR, "Cannot bind to socket");
rc = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)) < 0)
err(EX_OSERR, "Cannot setup socket options");
if (listen(fd, 25) < 0)
err(EX_OSERR, "Cannot setup listen length on socket");
return fd;
}
static int create_epoll(void) {
int fd;
if ((fd = epoll_create1(0)) < 0)
err(EX_OSERR, "Cannot create epoll");
return fd;
}
static bool epoll_join(int epollfd, int fd, int events) {
struct epoll_event ev;
ev.events = events;
ev.data.fd = fd;
if ((connections.numfds+1) >= connections.numevents) {
connections.numevents+=1024;
connections.events = realloc(connections.events,
sizeof(connections.events)*connections.numevents);
if (!connections.events)
err(EX_OSERR, "Cannot allocate memory for events list");
}
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
warn("Cannot add socket to epoll set");
return false;
}
connections.numfds++;
return true;
}
static void epoll_leave(int epollfd, int fd) {
if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL) < 0)
err(EX_OSERR, "Could not remove entry from epoll set");
connections.numfds--;
}
static void cleanup_old_events(void) {
if ((connections.numevents - 1024) > connections.numfds) {
connections.numevents -= 1024;
connections.events = realloc(connections.events,
sizeof(connections.events)*connections.numevents);
}
}
static void disconnect(int fd) {
shutdown(fd, SHUT_RDWR);
close(fd);
return;
}
static bool read_and_reply(int fd) {
char buf[128];
int rc;
memset(buf, 0, sizeof(buf));
if ((rc = recv(fd, buf, sizeof(buf), 0)) <= 0) {
rc ? warn("Cannot read from socket") : 1;
return false;
}
if (send(fd, buf, rc, MSG_NOSIGNAL) < 0) {
warn("Cannot send to socket");
return false;
}
return true;
}
int main()
{
int srv = create_srv_socket("8558");
int ep = create_epoll();
int rc = -1;
struct epoll_event *ev = NULL;
if (!epoll_join(ep, srv, EPOLLIN))
err(EX_OSERR, "Server cannot join epollfd");
while (1) {
int i, cli;
rc = epoll_wait(ep, connections.events, connections.numfds, -1);
if (rc < 0 && errno == EINTR)
continue;
else if (rc < 0)
err(EX_OSERR, "Cannot properly perform epoll wait");
for (i=0; i < rc; i++) {
ev = &connections.events[i];
if (ev->data.fd != srv) {
if (ev->events & EPOLLIN) {
if (!read_and_reply(ev->data.fd)) {
epoll_leave(ep, ev->data.fd);
disconnect(ev->data.fd);
}
}
if (ev->events & EPOLLERR || ev->events & EPOLLHUP) {
if (ev->events & EPOLLERR)
warn("Error in in fd: %d", ev->data.fd);
else
warn("Closing disconnected fd: %d", ev->data.fd);
epoll_leave(ep, ev->data.fd);
disconnect(ev->data.fd);
}
}
else {
if (ev->events & EPOLLIN) {
if ((cli = accept(srv, NULL, 0)) < 0) {
warn("Could not add socket");
continue;
}
epoll_join(ep, cli, EPOLLIN);
}
if (ev->events & EPOLLERR || ev->events & EPOLLHUP)
err(EX_OSERR, "Server FD has failed", ev->data.fd);
}
}
cleanup_old_events();
}
}
这是客户端:
from socket import *
import time
scks = list()
for i in range(0, 3000):
s = socket(AF_INET, SOCK_STREAM)
s.connect(("localhost", 8558))
scks.append(s)
time.sleep(600)
在我的本地机器上运行它时,我使用端口 8558 获得 6001 个套接字(1 个侦听、3000 个客户端套接字和 3000 个服务器端套接字)。
$ ss -ant | grep 8558 | wc -l
6001
检查客户端上连接的 IP 连接数时,我得到 3000。
# lsof -p$(pgrep python) | grep IPv4 | wc -l
3000
我也成功地在远程机器上尝试了服务器的测试。
我建议你尝试做同样的事情。
此外,请尝试完全关闭 iptables,以防万一出现连接跟踪问题。有时 iptables 选项/proc
也可以提供帮助。所以试试sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1
。
编辑:我做了另一个测试,它产生了你在你身边看到的输出。您的问题是您先发制人地关闭了服务器端的连接。
我可以复制类似于您在执行以下操作时看到的结果:
- 将一些数据读入我的服务器后,调用
shutdown(fd, SHUT_RD)
.
send(fd, buf, sizeof(buf))
在服务器上做。
执行此操作后,将看到以下行为。
- 在客户端上,我通过 ESTABLISHED 在 netstat/ss 中打开了 3000 个连接。
- 在 lsof 输出中,我建立了 2880 个(我关闭时的性质)连接。
- 其余连接
lsof -i:8558 | grep -v ES
处于 CLOSE_WAIT 中。
这只发生在半关机连接上。
因此,我怀疑这是您的客户端或服务器程序中的错误。您正在向服务器反对的服务器发送某些内容,或者服务器由于某种原因无效地关闭了连接。
您需要确认“异常”连接处于什么状态(如 close_wait 或其他)。
在这个阶段,我还认为这是一个编程问题,而不是真正属于 serverfault 的问题。如果不查看客户端/服务器源的相关部分,任何人都无法追踪故障原因。尽管我非常有信心这与操作系统处理连接的方式无关。