可以使用select、poll或epoll等多路复用机制,避免accept()函数的限制。以下是使用epoll的示例代码:
#include
// 创建epoll模型
int epollfd = epoll_create(MAX_CLIENTS);
// 监听socket加入epoll监听列表
struct epoll_event ev;
ev.data.fd = listenfd;
ev.events = EPOLLIN | EPOLLET; // 监听读事件,采用ET模式
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);
while (true) {
// 监听事件
struct epoll_event events[MAX_EVENTS];
int ret = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (ret < 0) {
perror("epoll_wait");
break;
}
for (int i = 0; i < ret; ++i) {
int fd = events[i].data.fd;
if (fd == listenfd) { // 监听socket有新连接
while (true) {
int connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
if (connfd < 0) { // 没有新连接或发生错误
if (errno == EAGAIN || errno == EWOULDBLOCK) { // ET模式,需要sockfd设置为非阻塞
break;
} else {
perror("accept");
break;
}
}
// 新连接加入epoll监听列表
struct epoll_event ev;
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev);
}
} else { // 已连接socket有数据到达
int nread = 0;
char buf[MAX_LINE];
while ((nread = read(fd, buf, MAX_LINE)) > 0) {
if (write(fd, buf, nread) < 0) {
perror("write");
return 1;
}
}
if (nread == 0) { // 客户端关闭连接
epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
} else if (nread < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read");
epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
}
}
}