SO_REUSEPORT端口重用

int opt_val = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &opt_val, sizeof(opt_val))
  • SO_REUSEPORT是允许多个socket绑定到同一个ip+port上

  • 每一个线程拥有自己的服务器套接字, 在服务器套接字上没有了锁的竞争

  • 内核层面实现负载均衡

SO_REUSEPORT在以下场景下有用:

  1. 当服务器需要使用多个进程或线程来监听相同的端口,而不想遇到惊群现象或资源竞争时,可以设置SO_REUSEPORT选项,以便实现网络连接的负载均衡。

  2. 当服务器需要使用UDP协议来接收多播数据报时,可以设置SO_REUSEPORT选项,以便让每个进程或线程接收到不同的数据报,提高效率和性能。

  3. EPOLL惊群问题


SO_REUSEADDR是地址重用

int opt_val = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val))
  • SO_REUSEADDR可以解决服务器主动关闭连接后,需要等待2MSL时间才能重新绑定相同的端口的问题。如果不设置这个选项,服务器在关闭连接后,会进入TIME_WAIT状态,此时再次启动服务器,就会绑定失败,报:Address already in use。如果设置了这个选项,服务器就可以立即重启并绑定相同的端口

  • SO_REUSEADDR可以解决服务器使用通配符IP地址(0.0.0.0)监听端口后,无法再绑定具体的IP地址到相同的端口的问题。如果不设置这个选项,当一个socket绑定到0.0.0.0:port后,其他socket就无法绑定到192.168.0.1:port等具体的IP地址上。如果设置了这个选项,就可以允许多个socket绑定到相同的端口,只要它们使用不同的本地IP地址

SO_REUSEADDR在以下场景下有用:

  1. 当服务器需要频繁地启动和关闭,而不想等待2MSL时间才能重新绑定相同的端口时,可以设置SO_REUSEADDR选项,以便快速重启。

  2. 当服务器需要使用通配符IP地址(0.0.0.0)监听端口,同时又想绑定其他具体的IP地址到相同的端口时,可以设置SO_REUSEADDR选项,以便支持多个IP地址的监听。


Epoll惊群

jingqun

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#define PROCESS_NUM 10
static int create_and_bind (char *port)
{
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(atoi(port));
    bind(fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
    return fd;
}

static int make_socket_non_blocking (int sfd)
{
    int flags, s;

    flags = fcntl (sfd, F_GETFL, 0);
    if (flags == -1)
    {
        perror ("fcntl");
        return -1;
    }

    flags |= O_NONBLOCK;
    s = fcntl (sfd, F_SETFL, flags);
    if (s == -1)
    {
        perror ("fcntl");
        return -1;
    }

    return 0;
}

#define MAXEVENTS 64

int main (int argc, char *argv[])
{
    int sfd, s;
    int efd;
    struct epoll_event event;
    struct epoll_event *events;

    sfd = create_and_bind("8001");
    if (sfd == -1)
        abort ();

    s = make_socket_non_blocking (sfd);
    if (s == -1)
        abort ();

    s = listen(sfd, SOMAXCONN);
    if (s == -1)
    {
        perror ("listen");
        abort ();
    }

    efd = epoll_create(MAXEVENTS);
    if (efd == -1)
    {
        perror("epoll_create");
        abort();
    }

    event.data.fd = sfd;
    //event.events = EPOLLIN | EPOLLET;
    event.events = EPOLLIN;
    s = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
    if (s == -1)
    {
        perror("epoll_ctl");
        abort();
    }

    /* Buffer where events are returned */
    events = calloc(MAXEVENTS, sizeof event);
    int k;
    for(k = 0; k < PROCESS_NUM; k++)
    {
        int pid = fork();
        if(pid == 0)
        {

            /* The event loop */
            while (1)
            {
                int n, i;
                n = epoll_wait(efd, events, MAXEVENTS, -1);
                printf("process %d return from epoll_wait!\n", getpid());
             for (i = 0; i < n; i++)
                {
                    if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN)))
                    {
                        /* An error has occured on this fd, or the socket is not ready for reading (why were we notified then?) */
                        fprintf (stderr, "epoll error\n");
                        close (events[i].data.fd);
                        continue;
                    }
                    else if (sfd == events[i].data.fd)
                    {
                        /* We have a notification on the listening socket, which means one or more incoming connections. */
                        struct sockaddr in_addr;
                        socklen_t in_len;
                        int infd;
                        char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

                        in_len = sizeof in_addr;
                        infd = accept(sfd, &in_addr, &in_len);
                        if (infd == -1)
                        {
                            printf("process %d accept failed!\n", getpid());
                            break;
                        }
                        printf("process %d accept successed!\n", getpid());

                        /* Make the incoming socket non-blocking and add it to the list of fds to monitor. */
                        close(infd);
                    }
                }
            }
        }
    }
    int status;
    wait(&status);
    free (events);
    close (sfd);
    return EXIT_SUCCESS;
}

Re:

https://blog.csdn.net/c359719435/article/details/51721902

https://www.cnblogs.com/schips/p/12553321.html

https://zhuanlan.zhihu.com/p/378892166

https://pureage.info/2015/12/22/thundering-herd.html

https://juejin.cn/post/6964714985364652039