【发布时间】:2015-08-22 04:51:12
【问题描述】:
假设我已经实现了一个基于 epoll 的 TCP 服务器,其中每个线程都在运行与下面非常相似的东西(取自 epoll 手册页,其中 kdpfd 是 epoll 文件描述符,侦听器是一个正在侦听端口的套接字):
struct epoll_event ev, *events;
for(;;) {
nfds = epoll_wait(kdpfd, events, maxevents, -1);
for(n = 0; n < nfds; ++n) {
if(events[n].data.fd == listener) {
client = accept(listener, (struct sockaddr *) &local,
&addrlen);
if(client < 0){
perror("accept");
continue;
}
setnonblocking(client);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%d0,
client);
return -1;
}
}
else
do_use_fd(events[n].data.fd);
}
}
对于上面的do_use_fd(events[n].data.fd),假设我们要将收到的所有内容写入标准输出:
int do_use_fd(int fd) {
int err;
char buf[512];
while ((err = read(fd, buf, 512)) > 0) {
write(1, buf, err);
}
if (err == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
// do some error handling and return -1
return 0;
}
现在,假设我有 10k+ 连接,所有这些连接都在很长一段时间内向我发送了大量消息。假设我的客户每隔几秒就向我发送消息hello, my name is {client's name}。假设(不知何故)这条消息足够大,以至于它必须作为多个数据包传输。
因此,read(fd, buf, 512) 可能偶尔会返回 -1 并带有 errno 指示它会阻塞。因此,我认为上述解决方案最终可能会得到以下输出:
hello, my nam
hello, my name is Pau
e is John Le
hello, my name is Geo
nnon
l McCartney
rge
hello, my name is Ringo
Starr
Harrison
因为一旦一个连接上的读取阻塞,另一个读取就可以在另一个连接上开始。相反,我希望打印以下内容:
hello, my name is John Lennon
hello, my name is Paul McCartney
hello, my name is George Harrison
hello, my name is Ringo Starr
是否有处理此问题的推荐方法?一种选择是为每个连接保留一个缓冲区,并检查消息是否已完成,并且仅在发生这种情况时打印。但是有 10k+ 个连接,这是个好主意吗?一方面,有些事情告诉我这个解决方案不能很好地扩展。另一方面,如果消息只有 500 字节,有 10k 连接,则此解决方案仅占用 5MB。
提前致谢。
【问题讨论】:
标签: c sockets scalability epoll