正确的前叉服务器套接字调用顺序

如果我这样做会发生什么(下面的伪代码):

s = socket bind s fork .... (... at child ...) listen s conn = accept s 

? 我应该使用:

 s = socket bind s listen s fork .... conn = accept s 

哪一个是正确的? 另外,我是否需要在套接字上为这个特定场景设置任何选项?

哪一个是正确的,在fork()之前或之后调用listen()

fork() listen() 之前调用listen() fork()是正确的。 listen()的作用是将底层套接字标记为准备与连接积压进行连接。 它只需要调用一次。

我从代码质量的角度将另一种方法标记为“不正确”,因为这是多余的和混乱的。

虽然反复调用listen()并不是一件坏事,但它很可疑。 规范没有说明在进行后续调用时会发生什么情况,只是面向连接的套接字应该“维护一个未完成连接指示的队列” ,也就是未决连接的积压。 随后的调用是否能够改变积压的大小? 你想要吗? 的确,并不是所有的操作系​​统甚至都可以查询积压的大小(FreeBSD有,例如SO_LISTENQLIMIT )。

在Linux上(从3.9开始)( Mac OS和FreeBSD以及其他的),你也可以选择使用SO_REUSEPORT

 // _DEFAULT_SOURCE for htobe16 for the port number, you may need _BSD_SOURCE instead #define _DEFAULT_SOURCE #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <unistd.h> #include <endian.h> int main() { struct sockaddr_in6 sa; int v = 1; // prepare ipv6 address [::]:1345 memset(&sa, 0, sizeof(sa)); sa.sin6_family = AF_INET6; sa.sin6_port = htobe16(1345); int s = socket(AF_INET6, SOCK_STREAM, 0); perror("socket"); // the key point: enable SO_REUSEPORT setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)); perror("setsockopt"); // from this point on just plain old socket use bind(s, (struct sockaddr*)&sa, sizeof(sa)); perror("bind"); listen(s, 0); perror("listen"); while (1) { int conn = accept(s, NULL, NULL); perror("accept"); close(conn); perror("close"); } return 0; } 

这种方法的优点是在过程之间不需要父母/子女关系。 另外,manpage( setsockopt(7) )表明,这比传统的方法有了性能改进。

在这种情况下,您可以调用socket 之前分叉。 唯一的要求是所有涉及到的进程在它们的套接字上设置SO_REUSEPORT并共享相同的有效UID。