我正在用套接字测试东西,我遇到了奇怪的情况:
我用c编写了非常简单的tcp服务器,我在accept()之后将其阻止,只是为了看看同时接受多个连接尝试时会发生什么:
这是服务器代码的摘录:
//listen()
if( (listen(sock,5)) == -1) {
perror("listen");
exit(-1);
}
//accept()
if( (cli = accept(sock, (struct sockaddr *) &client, &len)) == 1 ){
perror("accept");
exit(-1);
}
printf("entrez un int : ");
scanf("%d",&toto);
当服务器要求用户输入一个整数时,我尝试使用 telnet 连接多个客户端。
堡垒第一个,一切都好:
root@[...] :/home/[...]/workspace/sockets# netstat -antp | grep 10003
tcp 0 0 0.0.0.0:10003 0.0.0.0:* LISTEN 25832/toto
tcp 0 0 127.0.0.1:10003 127.0.0.1:51166 ESTABLISHED 25832/toto
tcp 0 0 127.0.0.1:51166 127.0.0.1:10003 ESTABLISHED 25845/telnet
但在第一个之后,即使我是 root,也有一些连接我看不到拥有它的进程及其 pid :
root@[...] :/home/[...]/workspace/sockets# netstat -antp | grep 10003
tcp 0 0 0.0.0.0:10003 0.0.0.0:* LISTEN 25832/toto
tcp 0 0 127.0.0.1:10003 127.0.0.1:51166 ESTABLISHED 25832/toto
tcp 0 0 127.0.0.1:51166 127.0.0.1:10003 ESTABLISHED 25845/telnet
tcp 0 0 127.0.0.1:10003 127.0.0.1:51168 ESTABLISHED -
tcp 0 0 127.0.0.1:51168 127.0.0.1:10003 ESTABLISHED 25852/telnet
第三个:
root@[...] :/home/[...]/workspace/sockets# netstat -antp | grep 10003
tcp 0 0 0.0.0.0:10003 0.0.0.0:* LISTEN 25832/toto
tcp 0 0 127.0.0.1:10003 127.0.0.1:51166 ESTABLISHED 25832/toto
tcp 0 0 127.0.0.1:51166 127.0.0.1:10003 ESTABLISHED 25845/telnet
tcp 0 0 127.0.0.1:10003 127.0.0.1:51172 ESTABLISHED -
tcp 0 0 127.0.0.1:10003 127.0.0.1:51168 ESTABLISHED -
tcp 0 0 127.0.0.1:51168 127.0.0.1:10003 ESTABLISHED 25852/telnet
tcp 0 0 127.0.0.1:51172 127.0.0.1:10003 ESTABLISHED 25860/telnet
几天后,我再次尝试使用 netstat -antpe 作为 root,这就是我得到的结果:
root@[...] :/home/[...]/workspace/sockets# netstat -antpe | grep 10003
tcp 0 0 0.0.0.0:10003 0.0.0.0:* LISTEN 1000 327680 22399/toto
tcp 0 0 127.0.0.1:33286 127.0.0.1:10003 ESTABLISHED 1000 417202 22884/telnet
tcp 0 0 127.0.0.1:10003 127.0.0.1:33046 ESTABLISHED 0 0 -
tcp 0 0 127.0.0.1:10003 127.0.0.1:33286 ESTABLISHED 0 0 -
tcp 0 0 127.0.0.1:33044 127.0.0.1:10003 ESTABLISHED 1000 332810 22402/telnet
tcp 0 0 127.0.0.1:33046 127.0.0.1:10003 ESTABLISHED 1000 331200 22410/telnet
tcp 0 0 127.0.0.1:10003 127.0.0.1:33044 ESTABLISHED 1000 332801 22399/toto
为什么进程或连接的 inode 可以为 0 ?有人可以解释一下发生了什么事吗?
答案1
服务器代码accept()
仅调用一次。因此,只有第一次连接尝试被有效地接受,其余的客户端连接保留在内核空间中的连接请求队列中。当accept()
再次调用时,将从队列中检索下一个客户端连接。
当客户端连接保留在内核空间中时,没有进程拥有客户端连接,因为如果多个进程或线程在所有参与套接字描述符上启用选项,则它们可以合法地接受来自唯一地址和端口对的连接SO_REUSEPORT
。
您可以SO_REUSEPORT
通过在调用之前添加以下代码片段bind()
并运行多个服务器来自行测试该选项。您会发现内核将在它们之间分配请求。
{
int enabled = -1;
if (setsockopt (sockd, SOL_SOCKET, SO_REUSEPORT,
(void*) &enabled, sizeof (enabled)) < 0) {
perror ("setsockopt");
}
}
参考自man 2 accept
:
这接受(sockfd)系统调用与基于连接的套接字类型(SOCK_STREAM、SOCK_SEQPACKET)一起使用。它提取侦听套接字的挂起连接队列上的第一个连接请求,sockfd,创建一个新的已连接套接字,并返回引用该套接字的新文件描述符。新创建的socket不处于监听状态。原来的插座sockfd不受此调用的影响。
参考自man 7 socket
:
SO_REUSEPORT(自 Linux 3.9 起)
允许将多个 AF_INET 或 AF_INET6 套接字绑定到相同的套接字地址。在调用之前必须在每个套接字(包括第一个套接字)上设置此选项绑定(2)在插座上。为了防止端口劫持,绑定到同一地址的所有进程必须具有相同的有效 UID。此选项可用于 TCP 和 UDP 套接字。
对于 TCP 套接字,此选项允许接受(2)通过为每个线程使用不同的侦听器套接字,可以改善多线程服务器中的负载分配。与传统技术相比,这提供了改进的负载分配,例如使用单个接受(2)线程来分配连接,或者使用多个线程来竞争接受(2)来自同一个插座。
对于 UDP 套接字,与让多个进程竞争在同一套接字上接收数据报的传统技术相比,使用此选项可以更好地将传入数据报分发到多个进程(或线程)。