Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用kqueue的TCP服务器工作者_C_Sockets_Tcp_Kqueue_Kevent - Fatal编程技术网

使用kqueue的TCP服务器工作者

使用kqueue的TCP服务器工作者,c,sockets,tcp,kqueue,kevent,C,Sockets,Tcp,Kqueue,Kevent,我最近对内核事件进行了一些测试,并得出以下结论: 使用内核事件接受套接字有意义吗?我的测试表明,我一次只能处理一个accept(即使eventlist数组更大)(因为ident==sockfd只对一个套接字有效) 我认为kevent的使用主要是一次读取多个套接字。这是真的吗 这就是TCP服务器使用kqueue实现的方式吗 侦听线程(不带队列) 接受新连接并将FD添加到工作队列。 问题:这可能吗?我的测试显示是的,但它是否保证工作线程会意识到这些更改,并且kevent真的是线程安全的

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

  • 使用内核事件接受套接字有意义吗?我的测试表明,我一次只能处理一个accept(即使eventlist数组更大)(因为ident==sockfd只对一个套接字有效)

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

这就是TCP服务器使用kqueue实现的方式吗


  • 侦听线程(不带队列)
    • 接受新连接并将FD添加到工作队列。 问题:这可能吗?我的测试显示是的,但它是否保证工作线程会意识到这些更改,并且kevent真的是线程安全的

  • 工作线程(带kqueue)

    • 等待从侦听线程添加的文件描述符的读取
    问题:一次有多少套接字可以检查更新



谢谢

通常,您可以使用kqueue来替代线程。如果要使用线程,只需设置一个侦听线程和一个工作线程池,每个接受的连接有一个线程。这是一个简单得多的编程模型

在事件驱动的框架中,您可以将侦听套接字和所有接受的套接字都放入kqueue中,然后在事件发生时进行处理。接受套接字时,将其添加到kqueue,当套接字处理程序完成其工作时,它可以从kqueue中删除套接字。(后者通常不是必需的,因为关闭fd会自动从任何队列中删除任何关联事件。)

请注意,在kqueue中注册的每个事件都有一个
void*
userdata,可用于在事件触发时识别所需的操作。因此,不必每个事件队列都有唯一的事件处理程序;事实上,有各种各样的处理程序是很常见的。(例如,您可能还希望处理通过命名管道设置的控制通道。)


混合事件/线程模型当然是可能的;否则,您无法利用多核CPU。一种可能的策略是在生产者-消费者模型中将事件队列用作调度器。队列处理程序将直接处理侦听套接字上的事件,接受连接并将接受的fd添加到事件队列中。当客户端连接事件发生时,该事件将被发布到工作队列中以供以后处理。也可以有多个工作队列,每个线程一个,并让接受者猜测新连接应该放在哪个工作队列中,这可能是基于该线程的当前负载。

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

#包括//fprintf
#包含//kqueue
#包括//addrinfo
#包括//AF\u INET
#包含//套接字
#包括//断言
#包括//bzero
#包括//bool
#包括//关闭
int main(int argc,const char*argv[]
{
/*初始化服务器套接字*/
结构addrinfo提示,*res;
int-sockfd;
bzero(&提示,sizeof(提示));
hits.ai_family=AF_INET;
hits.ai_socktype=SOCK_流;
断言(getaddrinfo(“localhost”、“9090”、“提示和res)==0);
sockfd=socket(AF\u INET,SOCK\u STREAM,res->ai\u协议);
断言(sockfd>0);
{
无符号opt=1;
断言(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))=0);
#ifdef SO_重用端口
断言(setsockopt(sockfd,SOL_SOCKET,SO_REUSEPORT,&opt,sizeof(opt))=0);
#恩迪夫
}
断言(绑定(sockfd,res->ai_addr,res->ai_addrlen)==0);
freeaddrinfo(res);
/*开始听*/
(无效)倾听(sockfd,5);
{
/*基文集*/
结构kevent-kevSet;
/*事件*/
struct kevent事件[20];
/*内文茨*/
未签名的nevents;
/*kq*/
int-kq;
/*缓冲区*/
char-buf[20];
/*长度*/
塞泽特·雷德伦;
kevSet.data=5;//backlog设置为5
kevSet.fflags=0;
kevSet.filter=EVFILT_READ;
kevSet.flags=EV_ADD;
kevSet.ident=sockfd;
kevSet.udata=NULL;
断言((kq=kqueue())>0);
/*更新kqueue*/
断言(kevent(kq,&kevSet,1,NULL,0,NULL)==0);
/*进入循环*/
while(true){
/*等待事件发生*/
nevents=kevent(kq,NULL,0,events,20,NULL);
断言(nevents>=0);
fprintf(stderr,“已处理%u个事件…\n”,nevents);
for(无符号i=0;i0);
/*添加到事件列表*/
kevSet.data=0;
kevSet.fflags=0;
kevSet.filter=EVFILT_READ;
kevSet.flags=EV_ADD;
kevSet.ident=nclientfd;
kevSet.udata=NULL;
断言(kevent(kq,&kevSet,1,NULL,0,NULL)==0);
fprintf(stderr,“连接到服务器的新客户端…\n”);
(void)写入(nclientfd,“欢迎使用此服务器!\n”,24);