网络原理-服务器百万并发实现

百万并发:能够同时承载的客户端数量是100万。
模拟客户端不断建立连接。

一、修改客户端和服务端 open files

  • 通过增大 open files 的限制,可以让一个进程同时打开更多的文件。
  • 当有很多用户请求到来时,每个请求都需要占用一个文件描述符。如果默认限制较低,可能会导致无法接受新的连接请求,从而影响服务器性能。
  • 通过使用 ulimit -n 命令增大 open files 的限制,可以提高服务器的并发处理能力
ulimit -a # 查看服务器限制参数
ulimit -n 1048576 # 修改 open files 为 100 万

在这里插入图片描述
在这里插入图片描述


二、修改服务端连接数组长度

struct conn_item conn_list[1048576] = {0};

三、修改客户端和服务端系统配置

sudo vim /etc/sysctl.conf
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_mem = 262144 524288 786432
net.ipv4.tcp_wmem = 1024 1024 2048
net.ipv4.tcp_rmem = 1024 1024 2048
fs.file-max = 1048576
net.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_estableished = 1200
sudo sysctl -p

四、服务端开启多个端口

  • 限制连接数量的是五元组
(sip, dip, sport, dport, proto)
(源ip, 目的ip, 源端口, 目的端口, 协议)
#include <sys/socket.h>
#include <sys/epoll.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/time.h>

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define BUFFER_LENGTH 128

typedef int (*RCALLBACK)(int fd);
// listenfd 触发 EPOLLIN 事件的时候,执行 accept_cb
int accept_cb(int fd);
// clientfd 触发 EPOLLIN 事件的时候,执行 recv_cb
int recv_cb(int fd);
// clientfd 触发 EPOLLOUT 事件的时候,执行 send_cb
int send_cb(int fd);

struct conn_item
{
    int fd;
    char rbuffer[BUFFER_LENGTH];
    int rlen;
    char wbuffer[BUFFER_LENGTH];
    int wlen;

    union
    {
        RCALLBACK accept_callback;
        RCALLBACK recv_callback;
    } recv_t;

    RCALLBACK send_callback;
};

int epfd = 0;
struct conn_item conn_list[1048576] = {0};
struct timeval start_time;
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)

int set_event(int fd, int event, int flag)
{
    if (flag)
    { // 1: add
        struct epoll_event ev;
        ev.events = event;
        ev.data.fd = fd;
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
    }
    else
    { // 0: mod
        // 设置事件
        struct epoll_event ev;
        ev.events = event;
        ev.data.fd = fd;
        epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
    }
}

int accept_cb(int fd)
{
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);
    int clientfd = accept(fd, (struct sockaddr *)&client_addr, &len);
    if (clientfd < 0)
    {
        return -1;
    }

    set_event(clientfd, EPOLLIN, 1);

    conn_list[clientfd].fd = clientfd;
    memset(conn_list[clientfd].rbuffer, 0, BUFFER_LENGTH);
    conn_list[clientfd].rlen = 0;
    memset(conn_list[clientfd].wbuffer, 0, BUFFER_LENGTH);
    conn_list[clientfd].wlen = 0;
    conn_list[clientfd].recv_t.recv_callback = recv_cb;
    conn_list[clientfd].send_callback = send_cb;

    if ((clientfd % 1000) == 999) {
        struct timeval cur_time;
        gettimeofday(&cur_time, NULL);
        int time_used = TIME_SUB_MS(cur_time, start_time);
        memcpy(&start_time, &cur_time, sizeof(struct timeval));

        printf("clientfd: %d, time_used: %d\n", clientfd, time_used);
    }
    return clientfd;
}

int recv_cb(int fd)
{
    char *buffer = conn_list[fd].rbuffer;
    int idx = conn_list[fd].rlen;

    int count = recv(fd, buffer + idx, BUFFER_LENGTH - idx, 0);
    if (count == 0)
    {
        printf("disconnect \n");

        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);

        close(fd);
        return -1;
    }
    conn_list[fd].rlen += count;

    memcpy(conn_list[fd].wbuffer, conn_list[fd].rbuffer, conn_list[fd].rlen);
    conn_list[fd].wlen = conn_list[fd].rlen;
    conn_list[fd].rlen -= conn_list[fd].rlen;

    set_event(fd, EPOLLOUT, 0);

    return count;
}

int send_cb(int fd)
{
    char *buffer = conn_list[fd].wbuffer;
    int idx = conn_list[fd].wlen;

    int count = send(fd, buffer, idx, 0);

    set_event(fd, EPOLLIN, 0);

    return count;
}

int init_server(unsigned short port)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)))
    {
        perror("bind");
        return -1;
    }

    listen(sockfd, 10);

    return sockfd;
}

int main()
{
    int port_count = 10;
    unsigned short port = 2048;
    int i = 0;
    epfd = epoll_create(1);
    for (i = 0; i < port_count; i++)
    {
        int sockfd = init_server(port + i); // 2048、2049、2050...2057
        conn_list[sockfd].fd = sockfd;
        conn_list[sockfd].recv_t.accept_callback = accept_cb;
        set_event(sockfd, EPOLLIN, 1);
    }

    gettimeofday(&start_time, NULL);

    struct epoll_event events[1024] = {0};

    while (1)
    {
        int nready = epoll_wait(epfd, events, 1024, -1);
        int i = 0;
        for (i = 0; i < nready; i++)
        {
            int connfd = events[i].data.fd;
            if (events[i].events & EPOLLIN)
            {
                int count = conn_list[connfd].recv_t.recv_callback(connfd);
            }
            else if (events[i].events & EPOLLOUT)
            {
                int count = conn_list[connfd].send_callback(connfd);
            }
        }
    }

    getchar();

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值