服务器百万并发(基于reactor模式)

服务端代码



#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>

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

#include <pthread.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <sys/time.h>


#define BUFFER_LENGTH		512

typedef int (*RCALLBACK)(int fd);

// listenfd
// EPOLLIN -->
int accept_cb(int fd);
// clientfd
//
int recv_cb(int fd);
int send_cb(int fd);

// conn, fd, buffer, callback
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;
};
// libevent -->


int epfd = 0;
struct conn_item connlist[1048576] = {0}; // 1024  2G     2 * 512 * 1024 * 1024
// list
struct timeval zvoice_king;
//
// 1000000

#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, 0 mod
        struct epoll_event ev;
        ev.events = event ;
        ev.data.fd = fd;
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
    } else {

        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 clientaddr;
    socklen_t len = sizeof(clientaddr);

    int clientfd = accept(fd, (struct sockaddr*)&clientaddr, &len);
    if (clientfd < 0) {
        return -1;
    }
    set_event(clientfd, EPOLLIN, 1);

    connlist[clientfd].fd = clientfd;
    memset(connlist[clientfd].rbuffer, 0, BUFFER_LENGTH);
    connlist[clientfd].rlen = 0;
    memset(connlist[clientfd].wbuffer, 0, BUFFER_LENGTH);
    connlist[clientfd].wlen = 0;

    connlist[clientfd].recv_t.recv_callback = recv_cb;
    connlist[clientfd].send_callback = send_cb;

    if ((clientfd % 1000) == 999) {
        struct timeval tv_cur;
        gettimeofday(&tv_cur, NULL);
        int time_used = TIME_SUB_MS(tv_cur, zvoice_king);

        memcpy(&zvoice_king, &tv_cur, sizeof(struct timeval));

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

    return clientfd;
}

int recv_cb(int fd) { // fd --> EPOLLIN

    char *buffer = connlist[fd].rbuffer;
    int idx = connlist[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;
    }
    connlist[fd].rlen += count;

#if 1 //echo: need to send
    memcpy(connlist[fd].wbuffer, connlist[fd].rbuffer, connlist[fd].rlen);
    connlist[fd].wlen = connlist[fd].rlen;
    connlist[fd].rlen -= connlist[fd].rlen;
#else

    //http_request(&connlist[fd]);
	//http_response(&connlist[fd]);

#endif

    set_event(fd, EPOLLOUT, 0);


    return count;
}

int send_cb(int fd) {

    char *buffer = connlist[fd].wbuffer;
    int idx = connlist[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 serveraddr;
    memset(&serveraddr, 0, sizeof(struct sockaddr_in));

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(port);

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

    listen(sockfd, 10);

    return sockfd;
}

// tcp
int main() {

    int port_count = 20;
    unsigned short port = 2048;
    int i = 0;


    epfd = epoll_create(1); // int size

    for (i = 0;i < port_count;i ++) {
        int sockfd = init_server(port + i);  // 2048, 2049, 2050, 2051 ... 2057
        connlist[sockfd].fd = sockfd;
        connlist[sockfd].recv_t.accept_callback = accept_cb;
        set_event(sockfd, EPOLLIN, 1);
    }

    gettimeofday(&zvoice_king, NULL);

    struct epoll_event events[1024] = {0};

    while (1) { // mainloop();

        int nready = epoll_wait(epfd, events, 1024, -1); //

        int i ;
        for (i = 0;i < nready;i ++) {

            int connfd = events[i].data.fd;
            if (events[i].events & EPOLLIN) { //

                int count = connlist[connfd].recv_t.recv_callback(connfd);
                //printf("recv count: %d <-- buffer: %s\n", count, connlist[connfd].rbuffer);

            } else if (events[i].events & EPOLLOUT) {
                // printf("send --> buffer: %s\n",  connlist[connfd].wbuffer);

                int count = connlist[connfd].send_callback(connfd);
            }

        }

    }


    getchar();
    //close(clientfd);

}




客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>

int main(int argc, char **argv){
    if(argc <=  2){
        printf("Usage: %s ip port\n", argv[0]);
        exit(0);
    }

    struct sockaddr_in addr;
    const char *ip = argv[1];
    int base_port = atoi(argv[2]);
    int opt = 1;
    int bufsize;
    socklen_t optlen;
    int connections = 0;

    memset(&addr, sizeof(addr), 0);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &addr.sin_addr);

    char tmp_data[10];

    int index = 1000;
    while(1){
        int port = index;
        index++;
        //printf("connect to %s:%d\n", ip, base_port);
        addr.sin_port = htons((short)base_port);
        int sock;
        if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1){
            goto sock_err;
        }
        if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1){
            goto sock_err;
        }
        connections ++;

        if(connections % 1000 == 999){
            printf("connections: %d, fd: %d\n", connections, sock);
        }
        usleep(1 * 1000);

        bufsize = 5000;
        setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
        setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
    }

    return 0;
    sock_err:
    printf("connections: %d\n", connections);
    printf("error: %s\n", strerror(errno));
    return 0;
}
第一个问题

当我们用服务端对接客户端的时候遇到的第一个问题就是打开的文件过多 导致程序挂掉

我们调用命令 ulimit -a 查看

open files 打开的文件数量上限只有1024个

我们调用 ulimit -n 1048576 命令

把打开文件的数量上限改成 一百万个

第二个问题 无法请求内存地址

我们先执行sudo vim /etc/sysctl.conf 命令

扫描二维码关注公众号,回复: 17597367 查看本文章

设置以下参数

net.ipv4.conf.all.log_martians:开启后,记录来源错误的 IP 包,1 表示启用。

net.ipv4.ip_local_port_range本地端口范围,1024 到 65535,客户端常用动态端口。

net.ipv4.tcp_memTCP 内存分配,分别是低、高、最大值,控制内存使用。

net.ipv4.tcp_wmemTCP 写缓冲区大小,默认、最小、最大值。

net.ipv4.tcp_rmemTCP 读缓冲区,类似写缓冲区的三个值。

fs.file-max系统全局最大打开文件数,控制资源。

net.netfilter.nf_conntrack_max连接跟踪表最大条目,防火墙相关。

net.netfilter.nf_conntrack_tcp_timeout_established已建立 TCP 连接的跟踪超时时间,1200 秒

之后我们执行 sudo sysctl -p 命令

可能会出现以下状况

我们执行sudo modprobe nf_conntrack 命令 加载内核模块

再输入sudo sysctl -p 命令

第三个问题虚拟机的配置

准备四台虚拟机 一台服务器配置高一些 三台客户机配置均衡配置 最后就是宿主机的性能比拼了