搭建一个后台服务器--服务端(异步,大并发)

本文转自:http://blog.csdn.net/xiaofei_hah0000/article/details/8743627

阻塞模式下服务器的并发只有几K,而真正的server 像nginx, apache, yumeiz 轻轻松松处理几万个并发完全不在话下,因此大并发的场合下是不能用阻塞的。

1W的并发是一个分隔点,如果单进程模型下能达到 的话,说明至少在服务器这块你已经很厉害了。

服务器开发就像一门气功,能不能搞出大并发,容错性处理得怎么样,就是你有没有内功,内功有多深。


异步模式是专门为大并发而生,linux下一般用 epoll 来管理事件,下面就开始我们的异步大并发服务器实战吧。

跟阻塞开发一样,先来看看设计过程:

1.创建事件模型。

2.创建监听连接并监听。

3.将监听连接加入事件模型。

4.当有事件时,判断事件类型。

5.若事件为监听连接,则产生客户连接同时加入事件模型,对客户连接接收发送。

6.若事件为客户连接,处理相应IO请求。


为了让大家一概全貌,我用一个函数实现的( 一个函数写一个2W并发的服务器,你试过么),可读性可能会差点,但是对付这道面试题是绰绰有余了。

实际开发过程如下:

先定义一个事件结构,用于对客户连接进行缓存

[cpp]  view plain copy
  1. struct my_event_s {
  2.     int fd;  
  3.     char recv[64];  
  4.     char send[64];  
  5.   
  6.     int rc_pos;  
  7.     int sd_pos;  
  8. };  

建立缓存对象:

[cpp]  view plain copy
  1. struct epoll_event wait_events[EPOLL_MAX];  
  2. struct my_event_s my_event[EPOLL_MAX];  

创建监听连接:

[cpp]  view plain copy
  1. sock_server = socket(AF_INET, SOCK_STREAM, 0);  
  2. flag = fcntl(sock_server, F_GETFL, 0);  
  3. fcntl(sock_server, F_SETFL, flag | O_NONBLOCK); /* 设定为监听套接口为非阻塞 */ 

绑定地址并监听:

[cpp]  view plain copy
  1. flag = bind(sock_server, (struct sockaddr *)&addr_server, sizeof(struct sockaddr));  
  2. if (flag < 0) {  
  3.     printf("your bind is not ok\n");  
  4.     close(sock_server);  
  5.     return 0;  
  6. }  
  7.   
  8. flag = listen(sock_server, 1024);  
  9. if (flag < 0) {  
  10.     printf("your listen is not ok\n");  
  11.     close(sock_server);  
  12.     return 0;  
  13. }
创建事件模型:
[cpp]  view plain copy
  1. epfd = epoll_create(EPOLL_MAX);  
  2. if (epfd <= 0) {  
  3.     printf("event module could not be setup\n");  
  4.     close(sock_server);  
  5.     return 0;  
  6. }  
将监听事件加入事件模型:
[cpp]  view plain copy
  1. tobe_event.events = EPOLLIN;  
  2. tobe_event.data.fd = sock_server;  
  3. epoll_ctl(epfd, EPOLL_CTL_ADD, sock_server, &tobe_event);  

事件模型处理:

[cpp]  view plain copy
  1. e_num = epoll_wait(epfd, wait_events, EPOLL_MAX, WAIT_TIME_OUT);  
  2. if (e_num <= 0) {  
  3.     continue;
  4. }
  5. for (i = 0; i < e_num; ++i) {
监听处理:
[cpp]  view plain copy
  1. if (sock_server == wait_events[i].data.fd) {
  2.     while (1) {

连接客户端:

[cpp]  view plain copy
  1. sock_client = accept(sock_server, (struct sockaddr *)&addr_client, (socklen_t *)&size);
  2. if (sock_client < 0) {
  3.     if (errno == EAGAIN) {  
  4.         break;
  5.     }
  6.     if (errno == EINTR) {
  7.         continue;  
  8.     }
  9.     break;
  10. }
将客户端连接设置为异步:
[cpp]  view plain copy
  1. flag = fcntl(sock_client, F_GETFL, 0);
  2. fcntl(sock_client, F_SETFL, flag | O_NONBLOCK);
将客户端连接加入到事件模型:
[cpp]  view plain copy
  1. tobe_event.events = EPOLLIN | EPOLLET; 
  2. tobe_event.data.u32 = my_empty_index;
  3. epoll_ctl(epfd, EPOLL_CTL_ADD, sock_client, &tobe_event);  
统计每秒并发数并得到系统当前时间:
[cpp]  view plain copy
  1. ++num;
  2. current = time(0);
  3. if (current > last) {
  4.     printf("last sec qps:%d\n", num);  
  5.     num = 0;
  6.     last = current;
  7. }
将时间填充到连接缓存中:
[cpp]  view plain copy
  1. memcpy(tobe_myevent->send, ¤t, sizeof(time_t));  

接收连接内容:

[cpp]  view plain copy
  1. flag = recv(sock_client, tobe_myevent->recv, 64, 0);  
  2. if (flag < 64) {
  3.     if (flag > 0) {
  4.         tobe_myevent->rc_pos += flag;
  5.     }
  6.     continue;
  7. }

[cpp]  view plain copy
  1. if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {  
  2.     printf("your recv does follow the protocal\n");  
  3.     tobe_myevent->fd = 0;  
  4.     close(sock_client);  
  5.     continue;  
  6. }

  7. flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);  
  8. if (flag < sizeof(time_t)) {
  9. <span style="white-space:pre">  </span>tobe_event.events =  EPOLLET | EPOLLOUT;  
  10. <span style="white-space:pre">  </span>epoll_ctl( epfd, EPOLL_CTL_MOD, sock_client, &tobe_event );  
  11.     if( flag > 0 )  
  12.         tobe_myevent->sd_pos += flag;  
  13.     continue;  
  14. }  
  15. tobe_myevent->fd = 0;  
  16. close( sock_client );  

后面进行普通连接事件处理,错误处理:

[cpp]  view plain copy
  1. if (event_flag | EPOLLHUP) {  
  2.     tobe_myevent->fd = 0;  
  3.     close( sock_client );  
  4.     continue;  
  5. else if (event_flag | EPOLLERR) {  
  6.     tobe_myevent->fd = 0;  
  7.     close( sock_client );  
  8.     continue;  
  9. }
写事件:
[cpp]  view plain copy
  1. else if (event_flag | EPOLLOUT) {  
  2.     if (tobe_myevent->rc_pos != 64) {  
  3.         continue;  
  4.     }  
  5.   
  6.     if (tobe_myevent->sd_pos >= sizeof(time_t)) {  
  7.         tobe_myevent->fd = 0;  
  8.         close( sock_client );  
  9.         continue;  
  10.     }
  11.   
  12.     flag = send(sock_client, tobe_myevent->send 
  13.                 + tobe_myevent->sd_pos, sizeof(time_t) - tobe_myevent->sd_pos, 0);
  14.     if (flag < 0) {  
  15.         if (errno == EAGAIN) {  
  16.             continue;  
  17.         } else if (errno == EINTR) {  
  18.             continue;  
  19.         }
  20.         tobe_myevent->fd = 0;  
  21.         close(sock_client);  
  22.         continue;  
  23.     }
  24.   
  25.     if (flag > 0) {  
  26.         tobe_myevent->sd_pos += flag;  
  27.         if (tobe_myevent->sd_pos >= sizeof(time_t)) {  
  28.             tobe_myevent->fd = 0;  
  29.             close(sock_client);  
  30.             continue;  
  31.         }
  32.     }
  33. }
读事件:
[cpp]  view plain copy
  1. if (event_flag | EPOLLIN) {  
  2.     if (tobe_myevent->rc_pos < 64) {  
  3.         flag = recv(sock_client, tobe_myevent->recv 
  4.                     + tobe_myevent->rc_pos, 64 - tobe_myevent->rc_pos, 0);
  5.         if (flag <= 0) {
  6.             continue;  
  7.         }

  8.         tobe_myevent->rc_pos += flag;  
  9.   
  10.         if (tobe_myevent->rc_pos < 64) {
  11.             continue;
  12.         }
  13.   
  14.         if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {
  15.             printf( "your recv does follow the protocal\n");
  16.             tobe_myevent->fd = 0;
  17.             close(sock_client);  
  18.             continue;  
  19.         }
  20.   
  21.         flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);
  22.         if (flag < sizeof(time_t)) {
  23.             if (flag > 0)
  24.                 tobe_myevent->sd_pos += flag;  
  25.         <span style="white-space:pre">  </span>tobe_event.events = EPOLLET | EPOLLOUT;  
  26.             tobe_event.data.u32 = wait_events[i].data.u32;  
  27.             epoll_ctl(epfd, EPOLL_CTL_MOD, sock_client, &tobe_event);  
  28.             continue;  
  29.         }  
  30.         tobe_myevent->fd = 0;  
  31.         close(sock_client);  
  32.     }  
  33.   
  34.     

到此,一个异步服务器搭建完毕,轻松实现2W的并发量,代码你可以在这里下载,下一篇是完整的代码,满足喜欢在网上看代码的码狂们。。。


完整代码如下:

#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>


#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

struct my_event_s {
    int fd;
    char recv[64];
    char send[64];
    int rc_pos;
    int sd_pos;
};

int main(int argc, char *argv[]) {
    int port;
    int flag;
    int size;
    int sock_server;
    int sock_client;
    time_t current;
    time_t last;
    int num;
    int e_num;
    int my_empty_index;
    int i,j;
    int event_flag;
    #define EPOLL_MAX 51200
    struct epoll_event wait_events[EPOLL_MAX];
    struct my_event_s my_event[EPOLL_MAX];
    struct my_event_s* tobe_myevent;
    struct epoll_event tobe_event;
    int epfd;

    #define RECV_BUF_LEN 256
    char buffer[RECV_BUF_LEN];

    struct sockaddr_in addr_server;
    struct sockaddr_in addr_client;

    if (argc <= 1) {
        printf("please set your port\n");
        return 0;
    }

    printf("your port:%s\n", argv[1]);
    port = atoi(argv[1]);

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

    sock_server = socket(AF_INET, SOCK_STREAM, 0);
    flag = fcntl(sock_server, F_GETFL, 0);
    fcntl(sock_server, F_SETFL, flag | O_NONBLOCK);

    flag = bind(sock_server, (struct sockaddr *)&addr_server, sizeof(struct sockaddr));
    if (flag < 0) {
        printf("your bind is not ok\n");
        close(sock_server);
        return 0;
    }

    flag = listen(sock_server, 1024);
    if (flag < 0) {
        printf("your listen is not ok\n");
        close(sock_server);
        return 0;
    }

    epfd = epoll_create(EPOLL_MAX);
    if (epfd <= 0) {
        printf("event module could not be setup\n");
        close(sock_server);
        return 0;
    }

    tobe_event.events = EPOLLIN;
    tobe_event.data.fd = sock_server;

    epoll_ctl(epfd, EPOLL_CTL_ADD, sock_server, &tobe_event);

    size = sizeof(addr_client);
    num = 0;
    last = 0;
    my_empty_index = 0;

    while (1) {
#define WAIT_TIME_OUT 600
        e_num = epoll_wait(epfd, wait_events, EPOLL_MAX, WAIT_TIME_OUT);
        if (e_num <= 0) {
            continue;
        }

        for (i = 0; i < e_num; ++i) {
            if (sock_server == wait_events[i].data.fd) {
                while (1) {
                    sock_client = accept(sock_server, (struct sockaddr *)&addr_client, (socklen_t *)&size);
                    if (sock_client < 0) {
                        if (errno == EAGAIN) {
    break;
                        }
                        if (errno == EINTR) {
                            continue;
                        }
                        break;
                    }

                    tobe_myevent = my_event + my_empty_index;
                    memset(tobe_myevent, 0, sizeof(struct my_event_s));
                    tobe_myevent->fd = sock_client;

                    flag = fcntl(sock_client, F_GETFL, 0);
                    fcntl(sock_client, F_SETFL, flag | O_NONBLOCK);

                    tobe_event.events = EPOLLIN | EPOLLET;
                    tobe_event.data.u32 = my_empty_index;

                    epoll_ctl(epfd, EPOLL_CTL_ADD, sock_client, &tobe_event);

                    for(j = my_empty_index + 1; j < EPOLL_MAX; ++j) {
                        if (!my_event[j].fd) {
                            my_empty_index = j;
                            break;
                        }
                    }

                    if (my_event[j].fd) {
                        for (j = 0; j < EPOLL_MAX; ++j) {
                            if (!my_event[j].fd) {
                                my_empty_index = j;
                                break;
                            }
                        }

                        if (my_event[j].fd) {
                            printf("your events has been none else\n");
                            close(sock_client);
                            close(sock_server);
                            return 0;
                        }
                    }

                    ++num;
                    current = time(0);
                    if (current > last) {
                        printf("last sec qps:%d\n", num);
                        num = 0;
                        last = current;
                    }

                    memcpy(tobe_myevent->send, ¤t, sizeof(time_t));

                    flag = recv(sock_client, tobe_myevent->recv, 64, 0 );
                    if (flag < 64) {
                        if (flag > 0)
                            tobe_myevent->rc_pos += flag;
                        continue;
                    }

                    if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {
                        printf("your recv does follow the protocal\n");
                        tobe_myevent->fd = 0;
                        close(sock_client);
                        continue;
                    }

                    flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);
                    if (flag < sizeof(time_t)) {
                        tobe_event.events =  EPOLLET | EPOLLOUT;
                        epoll_ctl(epfd, EPOLL_CTL_MOD, sock_client, &tobe_event);
                        if (flag > 0)
                            tobe_myevent->sd_pos += flag;
                        continue;
                    }
                    tobe_myevent->fd = 0;
                    close(sock_client);
                }
            } else {
                tobe_myevent = my_event + wait_events[i].data.u32;
                sock_client = tobe_myevent->fd;
                event_flag = wait_events[i].events;

                if (event_flag | EPOLLHUP) {
                    tobe_myevent->fd = 0;
                    close(sock_client);
                    continue;
                } else if( event_flag | EPOLLERR) {
                    tobe_myevent->fd = 0;
                    close(sock_client);
                    continue;
                } else if (event_flag | EPOLLOUT) {
                    if (tobe_myevent->rc_pos != 64) {
                        continue;
                    }


                    if (tobe_myevent->sd_pos >= sizeof(time_t)) {
                        tobe_myevent->fd = 0;
                        close(sock_client);
                        continue;
                    }


                    flag = send(sock_client, tobe_myevent->send + tobe_myevent->sd_pos, sizeof(time_t) - tobe_myevent->sd_pos, 0);
                    if (flag < 0) {
                        if (errno == EAGAIN) {
                            continue;
                        } else if( errno == EINTR) {
                            continue;
                        }
                        tobe_myevent->fd = 0;
                        close(sock_client);
                        continue;
                    }

                    if (flag > 0) {
                        tobe_myevent->sd_pos += flag;
                        if (tobe_myevent->sd_pos >= sizeof(time_t)) {
                            tobe_myevent->fd = 0;
                            close(sock_client);
                            continue;
                        }
                    }
                }
                if (event_flag | EPOLLIN) {
                    if (tobe_myevent->rc_pos < 64) {
                        flag = recv(sock_client, tobe_myevent->recv + tobe_myevent->rc_pos, 64 - tobe_myevent->rc_pos, 0);
                        if (flag <= 0) {
                            continue;
                        }

                        tobe_myevent->rc_pos += flag;

                        if (tobe_myevent->rc_pos < 64) {
                            continue;
                        }

                        if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {
                            printf("your recv does follow the protocal\n");
                            tobe_myevent->fd = 0;
                            close(sock_client);
                            continue;
                        }

                        flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);
                        if (flag < sizeof(time_t)) {
                            if (flag > 0)
                                tobe_myevent->sd_pos += flag;
                            tobe_event.events = EPOLLET | EPOLLOUT;
                            tobe_event.data.u32 = wait_events[i].data.u32;
                            epoll_ctl(epfd, EPOLL_CTL_MOD, sock_client, &tobe_event);
                            continue;
                        }
                        tobe_myevent->fd = 0;
                        close(sock_client);
                    }
                }
            }
        }
    }
    printf("close server connection\n");
    close(sock_server);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值