用epollmultithreadingUDP服务器?

我想在C / Linux中开发一个multithreading的UDP服务器。 该服务在单个端口x上运行,因此只有绑定一个UDP套接字的可能性。 为了在高负载下工作,我有n个线程(静态定义),比如每个CPU有1个线程。 工作可以通过epoll_wait传递给线程,所以线程可以根据需要用'EPOLLET |'唤醒 EPOLLONESHOT”。 我附上了一个代码示例:

static int epfd; static sig_atomic_t sigint = 0; ... /* Thread routine with epoll_wait */ static void *process_clients(void *pevents) { int rc, i, sock, nfds; struct epoll_event ep, *events = (struct epoll_event *) pevents; while (!sigint) { nfds = epoll_wait(epfd, events, MAX_EVENT_NUM, 500); for (i = 0; i < nfds; ++i) { if (events[i].data.fd < 0) continue; sock = events[i].data.fd; if((events[i].events & EPOLLIN) == EPOLLIN) { printf("Event dispatch!\n"); handle_request(sock); // do a recvfrom } else whine("Unknown poll event!\n"); memset(&ep, 0, sizeof(ep)); ep.events = EPOLLIN | EPOLLET | EPOLLONESHOT; ep.data.fd = sock; rc = epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &ep); if(rc < 0) error_and_die(EXIT_FAILURE, "Cannot add socket to epoll!\n"); } } pthread_exit(NULL); } int main(int argc, char **argv) { int rc, i, cpu, sock, opts; struct sockaddr_in sin; struct epoll_event ep, *events; char *local_addr = "192.168.1.108"; void *status; pthread_t *threads = NULL; cpu_set_t cpuset; threads = xzmalloc(sizeof(*threads) * MAX_THRD_NUM); events = xzmalloc(sizeof(*events) * MAX_EVENT_NUM); sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) error_and_die(EXIT_FAILURE, "Cannot create socket!\n"); /* Non-blocking */ opts = fcntl(sock, F_GETFL); if(opts < 0) error_and_die(EXIT_FAILURE, "Cannot fetch sock opts!\n"); opts |= O_NONBLOCK; rc = fcntl(sock, F_SETFL, opts); if(rc < 0) error_and_die(EXIT_FAILURE, "Cannot set sock opts!\n"); /* Initial epoll setup */ epfd = epoll_create(MAX_EVENT_NUM); if(epfd < 0) error_and_die(EXIT_FAILURE, "Error fetching an epoll descriptor!\n"); memset(&ep, 0, sizeof(ep)); ep.events = EPOLLIN | EPOLLET | EPOLLONESHOT; ep.data.fd = sock; rc = epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ep); if(rc < 0) error_and_die(EXIT_FAILURE, "Cannot add socket to epoll!\n"); /* Socket binding */ sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(local_addr); sin.sin_port = htons(port_xy); rc = bind(sock, (struct sockaddr *) &sin, sizeof(sin)); if (rc < 0) error_and_die(EXIT_FAILURE, "Problem binding to port! " "Already in use?\n"); register_signal(SIGINT, &signal_handler); /* Thread initialization */ for (i = 0, cpu = 0; i < MAX_THRD_NUM; ++i) { rc = pthread_create(&threads[i], NULL, process_clients, events); if (rc != 0) error_and_die(EXIT_FAILURE, "Cannot create pthread!\n"); CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); rc = pthread_setaffinity_np(threads[i], sizeof(cpuset), &cpuset); if (rc != 0) error_and_die(EXIT_FAILURE, "Cannot create pthread!\n"); cpu = (cpu + 1) % NR_CPUS_ON; } printf("up and running!\n"); /* Thread joining */ for (i = 0; i < MAX_THRD_NUM; ++i) { rc = pthread_join(threads[i], &status); if (rc != 0) error_and_die(EXIT_FAILURE, "Error on thread exit!\n"); } close(sock); xfree(threads); xfree(events); printf("shut down!\n"); return 0; } 

这是与epoll处理这种情况的正确方法? 函数_handle_request_应该尽可能快地返回,因为这个时候socket的eventqueue被阻塞了!

感谢您的回复!

Solutions Collecting From Web of "用epollmultithreadingUDP服务器?"

由于您只使用单个UDP套接字,因此使用epoll没有意义 – 只需使用阻塞recvfrom即可。

现在,根据您需要处理的协议 – 如果您可以单独处理每个UDP数据包 – 实际上可以从多个线程(在一个线程池中)同时调用recvfrom。 操作系统将注意到一个线程将会收到UDP数据包。 然后这个线程可以做任何需要在handle_request中做的事情。

但是,如果您需要按特定顺序处理UDP数据包,则可能没有那么多机会来平行执行程序。

不,这不会按照你想要的方式工作。 要使工作线程处理通过epoll接口到达的事件,您需要一个不同的体系结构。

示例设计(有几种方法可以做到这一点) 使用:SysV / POSIX信号量。

  • 让主线程产生n个子线程和一个信号量,然后阻塞你的套接字(或其他)。

  • 让每个子线程块都在信号量的下面。

  • 当主线程解除阻塞时,它将事件存储在某个全局结构中,并且每个事件都会启动一次信号量。

  • 子线程解除阻塞,处理事件,当信号量返回到0时再次阻塞。

您可以使用所有线程之间共享的管道来实现与信号量非常类似的功能。 这会让你阻塞select()而不是信号量,你可以用它来唤醒其他事件上的线程(超时,其他管道等)

您也可以反转这个控制,并且在其工作人员要求任务时让主线程唤醒。 不过,我认为上述方法对您的情况更好。