应用程序是否可以处理N个accept连接,并且每个accept都使用一个独立的线程
我有一个C linux TCP客户机/服务器应用程序。 我提出了一个奇怪的场景,但我不知道这个应用程序是否会产生任何后果。 我有一个可以接受N个连接的服务器端,例如,这个服务器将接受100个连接。 在这个场景中,我在主线程中创建侦听套接字,然后创建100个线程,每个线程都有一个独立的accept()和select()iomux,而且每个线程只能接受一个连接 我在这里关心的是,如果两个并发accept()因为select是同一套接字上的ready to read而想要接受同一套接字(连接),我不知道并发accept在内核中是否是线程安全的,并且只有一个accept可以处理传入的连接,而另一个将等待另一个连接 我在我的RedHat机器上试过,效果很好,但我不知道我是否幸运地避免了死锁 谢谢应用程序是否可以处理N个accept连接,并且每个accept都使用一个独立的线程,c,linux,sockets,linux-kernel,posix,C,Linux,Sockets,Linux Kernel,Posix,我有一个C linux TCP客户机/服务器应用程序。 我提出了一个奇怪的场景,但我不知道这个应用程序是否会产生任何后果。 我有一个可以接受N个连接的服务器端,例如,这个服务器将接受100个连接。 在这个场景中,我在主线程中创建侦听套接字,然后创建100个线程,每个线程都有一个独立的accept()和select()iomux,而且每个线程只能接受一个连接 我在这里关心的是,如果两个并发accept()因为select是同一套接字上的ready to read而想要接受同一套接字(连接),我不知
rc = bind(sd, (struct sockaddr_in *)& groupSock, sizeof(struct sockaddr_in));
CHECK_VALUE("Bind address error", rc, 0, goto cleanup);
rc = listen(sd, 10);
CHECK_VALUE("listen", rc, 0, goto cleanup);
for(; count< num_socks; count++){
par_data[count].sd = sd;
par_data[count].thread_num = count;
par_data[count].err_chk = -1;
rc = pthread_create(&thread_id[count], NULL, accept_sock_thread, (void *)& par_data[count]);
CHECK_VALUE("pthread_create", rc, 0, goto cleanup);
}
void * accept_sock_thread(void* atr){
int rc;
int sock = INVALID_SOCKET;
int datalen = config.traffic;
char *databuf = NULL;
struct thread_data *data = NULL;
struct sockaddr_in tcp_remote;
struct timeval t;
socklen_t size;
fd_set socks;
databuf = malloc(sizeof(char) * datalen);
memset(databuf, 0, datalen);
data = (struct thread_data*) atr;
DEBUG(my_debug_flags, ENTER_FUNCT, ("Enter Function accept_sock_thread thread_num %d \n", data->thread_num));
FD_ZERO(&socks);
FD_SET(data->sd, &socks);
t.tv_sec = 25;
t.tv_usec = 0;
rc = select(data->sd + 1 , &socks, NULL, NULL,&t);
if(rc < 0){
VL_MISC_ERR(("Error in select with Errno: %d", errno));
goto cleanup;
}
else if(rc == 0){
VL_MISC_ERR(("Accept Select returned a TIEMOUT."));
goto cleanup;
}
size = sizeof(struct sockaddr_in);
sock = accept(data->sd, (struct sockaddr *)& tcp_remote, &size);
CHECK_NOT_EQUAL("tcp accept error", sock, INVALID_SOCKET, goto cleanup);
cleanup:
// sleep(2); /* avoid EOF */
if(sock != INVALID_SOCKET){
rc = close(sock);
if(rc != 0){
data->err_chk = -1;
}
}
return NULL;
}
rc=bind(sd,(struct sockaddr_in*)和groupSock,sizeof(struct sockaddr_in));
检查_值(“绑定地址错误”,rc,0,转到清除);
rc=听(sd,10);
检查_值(“侦听”,rc,0,转到清除);
对于(;计数thread_num));
FD_零和袜子;
FD_集(数据->sd和socks);
t、 tv_sec=25;
t、 tv_usec=0;
rc=select(数据->sd+1,&socks,NULL,NULL,&t);
if(rc<0){
VL_MISC_ERR((“选择错误,错误号:%d”,错误号));
去清理;
}
else if(rc==0){
VL_MISC_ERR((“接受选择返回一个TIEMOUT”);
去清理;
}
size=sizeof(结构sockaddr_in);
sock=accept(数据->sd,(结构sockaddr*)和tcp_远程和大小);
检查“不相等”(“tcp接受错误”、套接字、无效套接字、转到清除);
清理:
//睡眠(2);/*避免EOF*/
if(sock!=无效的_套接字){
rc=关闭(sock);
如果(rc!=0){
data->err_chk=-1;
}
}
返回NULL;
}
根据POSIX标准,
accept()
是线程安全且可重入的
这意味着对同一描述符的两个accept调用不应给出未定义的行为。一个accept将打开套接字,另一个将返回错误
你可以在那里看到更多:
只有一个线程将接受连接。内核保证了这一点。在unix/posix世界中,这种方式已经存在很长时间了。只有一个线程会接受(),到目前为止还不错…-但在此之前,所有线程都将被触发,以返回
select()
,这可能不是您想要的
因此,如果您有N个线程睡在select()
中,并且有一个连接进入中,那么所有的线程都会被唤醒,但只需要一个,因为只有一个可以成功地接受()
调用。使用接受()
对来自多个线程/进程的同一套接字对象进行访问不仅是标准策略,而且也是广泛使用的策略。据我所知,apache就是这样做的nginx也会这样做,但方式略有不同
注意:select()
可以唤醒多个线程,但其中只有一个线程将接受连接,而其他线程将a挂起accept()
或b)返回-1,并在非阻塞IO的情况下将errno
设置为EAGAIN。由于您使用的是accept()之前的select()
,因此我假设套接字描述符处于非阻塞模式。因此,这可能会导致某些线程从未为连接提供服务
另外,我建议您不要在多个线程中关闭同一个套接字。这可能导致非常恶劣和难以调试的后果。使用包装器和
共享\u ptr
或将“套接字所有者”角色分配给其中一个线程。您可以发布处理连接的代码吗?您描述的场景可能很好,但也取决于实现sd=socket(AF_INET,SOCK_STREAM,0);rc=bind(sd,(struct sockaddr_in*)和groupSock,sizeof(struct sockaddr_in));rc=监听(sd,100);对于(;count