gpt4 book ai didi

c - 使用 kqueue 的 TCP 服务器 worker

转载 作者:可可西里 更新时间:2023-11-01 02:32:32 24 4
gpt4 key购买 nike

我最近对内核事件进行了一些测试,并得出以下结论:

  • 使用内核事件接受套接字是否有意义?我的测试表明我一次只能处理一个接受(即使事件列表数组更大)(对我来说很有意义,因为 .ident == sockfd 仅适用于一个套接字)。

  • 我以为kevent的使用主要是一次从多个套接字读取。是真的吗?

这就是 TCP 服务器通过 kqueue 实现的方式吗? :


  • 监听线程(没有kqueue)
    • 接受新连接并将 FD 添加到工作队列。问题:这有可能吗?我的测试显示是的,但它是否保证工作线程会知道这些变化并且 kevent 真的是线程安全的?

  • 工作线程(使用 kqueue)

    • 等待读取从监听线程添加的文件描述符。

    问题:一次检查多少个套接字才有意义?


谢谢

最佳答案

这不是真正的答案,但我用 kqueue 做了一个小服务器脚本来解释这个问题:

#include <stdio.h>          // fprintf
#include <sys/event.h> // kqueue
#include <netdb.h> // addrinfo
#include <arpa/inet.h> // AF_INET
#include <sys/socket.h> // socket
#include <assert.h> // assert
#include <string.h> // bzero
#include <stdbool.h> // bool
#include <unistd.h> // close

int main(int argc, const char * argv[])
{

/* Initialize server socket */
struct addrinfo hints, *res;
int sockfd;

bzero(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

assert(getaddrinfo("localhost", "9090", &hints, &res) == 0);

sockfd = socket(AF_INET, SOCK_STREAM, res->ai_protocol);

assert(sockfd > 0);

{
unsigned opt = 1;

assert(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0);

#ifdef SO_REUSEPORT
assert(setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) == 0);
#endif
}

assert(bind(sockfd, res->ai_addr, res->ai_addrlen) == 0);

freeaddrinfo(res);

/* Start to listen */
(void)listen(sockfd, 5);

{
/* kevent set */
struct kevent kevSet;
/* events */
struct kevent events[20];
/* nevents */
unsigned nevents;
/* kq */
int kq;
/* buffer */
char buf[20];
/* length */
ssize_t readlen;

kevSet.data = 5; // backlog is set to 5
kevSet.fflags = 0;
kevSet.filter = EVFILT_READ;
kevSet.flags = EV_ADD;
kevSet.ident = sockfd;
kevSet.udata = NULL;

assert((kq = kqueue()) > 0);

/* Update kqueue */
assert(kevent(kq, &kevSet, 1, NULL, 0, NULL) == 0);

/* Enter loop */
while (true) {
/* Wait for events to happen */
nevents = kevent(kq, NULL, 0, events, 20, NULL);

assert(nevents >= 0);

fprintf(stderr, "Got %u events to handle...\n", nevents);

for (unsigned i = 0; i < nevents; ++i) {
struct kevent event = events[i];
int clientfd = (int)event.ident;

/* Handle disconnect */
if (event.flags & EV_EOF) {

/* Simply close socket */
close(clientfd);

fprintf(stderr, "A client has left the server...\n");
} else if (clientfd == sockfd) {
int nclientfd = accept(sockfd, NULL, NULL);

assert(nclientfd > 0);

/* Add to event list */
kevSet.data = 0;
kevSet.fflags = 0;
kevSet.filter = EVFILT_READ;
kevSet.flags = EV_ADD;
kevSet.ident = nclientfd;
kevSet.udata = NULL;

assert(kevent(kq, &kevSet, 1, NULL, 0, NULL) == 0);

fprintf(stderr, "A new client connected to the server...\n");

(void)write(nclientfd, "Welcome to this server!\n", 24);
} else if (event.flags & EVFILT_READ) {

/* sleep for "processing" time */
readlen = read(clientfd, buf, sizeof(buf));

buf[readlen - 1] = 0;

fprintf(stderr, "bytes %zu are available to read... %s \n", (size_t)event.data, buf);

sleep(4);
} else {
fprintf(stderr, "unknown event: %8.8X\n", event.flags);
}
}
}
}

return 0;
}

每次客户端发送内容时,服务器都会经历 4 秒的“滞后”。 (我夸张了一点,但是对于测试来说还是挺合理的)。那么如何解决这个问题呢?我将带有自己的 kqueue 的工作线程(池)视为可能的解决方案,这样就不会发生连接延迟。 (每个工作线程读取一定“范围”的文件描述符)

关于c - 使用 kqueue 的 TCP 服务器 worker ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25228938/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com