美文网首页
SO_REUSEPORT

SO_REUSEPORT

作者: 小幸运Q | 来源:发表于2020-11-07 22:14 被阅读0次

    • <protocol>, <src addr>, <src port>, <dest addr>, <dest port>可以唯一地确一个连接,基于hashmap映射查找

    • 可见最大连接数是受linux最大可打开文件文件数限制的而不是端口数,因为最大可打开文件数可以通过修改系统配置文件更改,所以最终最大连接数的限制来自于系统的资源限制。

    在此之前的解决方案:
    (1)指派一条线程专门进行accept,获取socket后分派给worker线程。这种方法使得进行accept的线程成为了单点,容易成为性能的瓶颈。
    (2)多个线程同时进行accept。这种方法的问题是每一个线程accept成功的概率不均匀,导致负载不均衡。

    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT,(const void *)&reuse , sizeof(int));
    

    SO_REUSEPORT支持多个进程或者线程绑定到同一端口,提高服务器程序的性能,解决的问题:

    • 允许多个套接字 bind()/listen() 同一个TCP/UDP端口
      • 每一个线程拥有自己的服务器套接字
      • 在服务器套接字上没有了锁的竞争
    • 内核层面实现负载均衡
    • 安全层面,监听同一个端口的套接字只能位于同一个用户下面
    • 各个进程中accept socket fd不一样,有新连接建立时,内核只会唤醒一个进程来accept,并且保证唤醒的均衡性。
    #include <stdio.h>                                                                                                               
    #include <stdlib.h>
    #include <string.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <time.h>
    #include <unistd.h>
    #include <sys/wait.h>
    void work () {
            int listenfd = socket(AF_INET, SOCK_STREAM, 0);
            if (listenfd < 0) {
                    perror("listen socket");
                    _exit(-1);
            }
            int ret = 0;
            int reuse = 1;
            ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)&reuse , sizeof(int));
            if (ret < 0) {
                    perror("setsockopt");
                    _exit(-1);
            }
            ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT,(const void *)&reuse , sizeof(int));
            if (ret < 0) {
                perror("setsockopt");
                _exit(-1);
            }
            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(addr));
            addr.sin_family = AF_INET;
            //addr.sin_addr.s_addr = inet_addr("10.95.118.221");
            addr.sin_addr.s_addr = inet_addr("0.0.0.0");                                                                             
            addr.sin_port = htons(9980);
            ret = bind(listenfd, (struct sockaddr *)&addr, sizeof(addr));
            if (ret < 0) {
                    perror("bind addr");
                    _exit(-1);
           }
            printf("bind success\n");
            ret = listen(listenfd,10);
            if (ret < 0) {
                    perror("listen");
                    _exit(-1);
            }
            printf("listen success\n");
            struct sockaddr clientaddr;
            int len = 0;
            while(1) {
                    printf("process:%d accept...\n", getpid());
                    int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
                    if (clientfd < 0) {
                            printf("accept:%d %s", getpid(),strerror(errno));
                            _exit(-1);
                    }
                    close(clientfd);
                    printf("process:%d close socket\n", getpid());
            }
    }
    int main(){
            printf("uid:%d euid:%d\n", getuid(),geteuid());
            int i = 0;
            for (i = 0; i< 6; i++) {
                    pid_t pid = fork();
                    if (pid == 0) {
                            work();
                    }
                    if(pid < 0) {
                            perror("fork");
                            continue;
                    }
            }
            int status,id;
            while((id=waitpid(-1, &status, 0)) > 0) {
                    printf("%d exit\n", id);
            }
            if(errno == ECHILD) {
                    printf("all child exit\n");
            }
            return 0;
    }   
    

    相关文章

      网友评论

          本文标题:SO_REUSEPORT

          本文链接:https://www.haomeiwen.com/subject/lqfuvktx.html