Redis网络库源码分析(2)之启动服务器

一、从main开始

main函数定义在server.c中,它的内容如下:

//server.c

int main()
{
    signal(SIGPIPE, SIG_IGN);           
    //忽略SIGPIPE信号,防止给一个已经关闭socket的客户端连续两次发送数据导致SIGPIPE信号
    //的产生,它的默认做法是终止进程。
    server_t server;            //创建一个server
    bzero(&server, sizeof(server));   

    server.backlog = DEFAULT_LISTEN_BACKLOG;       //设置backlog的大小
    server.max_client_count = DEFAULT_MAX_CLIENT_COUNT;  //设置最大的客户端连接数
    server.port = DEFAULT_LISTEN_PORT;   //设置默认的监听端口

    init_server(&server);                //初始化
    wait_server(&server);                //实际上进入了loop循环

    return 0;
}

现在我们去看下定义的server_t 到底是什么东东?

//server.h

typedef struct {
    aeEventLoop *loop;           //最核心的时间循环
    int listen_fd;               //监听fd,socket函数返回
    int port;                    //默认的监听端口
    int backlog;                 //listen函数第二个参数backlog的大小
    int max_client_count;        //最大的客户端连接数
    char err_info[ANET_ERR_LEN]; //err信息
} server_t;

是一个非常简单的服务器定义。最核心的是 aeEventLoop,它是整个事件循环的结构体,我们现在看看它里面有什么:

//ae.h

/* State of an event based program */
typedef struct aeEventLoop {
    int maxfd;                  /* 当前注册的最大文件描述符 */
    int setsize;                /* 监控的最大文件描述符数 */
    long long timeEventNextId;  /* 定时事件ID */
    time_t lastTime;            /* 最近一次处理定时事件的时间 */
    aeFileEvent *events;        /* 注册事件链表 */
    aeFiredEvent *fired;        /* 发生事件链表 */
    aeTimeEvent *timeEventHead; /* 定时事件链表*/
    int stop;                   /* 是否停止循环*/
    void *apidata;              /* 特定接口的特定数据*/
    aeBeforeSleepProc *beforesleep; /*在sleep之前执行的程序*/
} aeEventLoop;


/* File event structure  事件结构体*/
typedef struct aeFileEvent {
    int mask;                /* 事件码:可读/可写 */
    aeFileProc *rfileProc;   /* 读事件的处理函数*/
    aeFileProc *wfileProc;   /* 写事件的处理函数*/
    void *clientData;        /* 用于传递server和client实例给相应函数*/
} aeFileEvent;

/* Time event structure  定时事件结构体*/
typedef struct aeTimeEvent {
    long long id;              /* 定时事件id */
    long when_sec;             /* 秒 */
    long when_ms;              /* 毫秒 */
    aeTimeProc *timeProc;      /* 定时事件处理程序*/
    aeEventFinalizerProc *finalizerProc;
    void *clientData;          /* 用于传递server和client实例给相应函数*/  
    struct aeTimeEvent *next;  /* 下一个节点 */
} aeTimeEvent;

/* A fired event  发生了事件的结构体*/    
typedef struct aeFiredEvent {
    int fd;                   /* fd */    
    int mask;                 /* 发生事件的掩码 (读/写)*/
} aeFiredEvent;

基本上这个结构体就能表示我们服务器在运行期间的数据结构了。

二、init_server 初始化server

void init_server(server_t *server)
{
    server->loop = aeCreateEventLoop(server->max_client_count);   
    /* 为loop中各类数据结构申请空间 */

    //aeCreateTimeEvent(loop, 1000, serverCron, NULL, NULL);

    server->listen_fd = anetTcpServer(server->err_info, server->port, NULL, server->backlog);
    /* 创建listen_fd 实际上调用socket函数 */

    if (server->listen_fd != ANET_ERR) {
        anetNonBlock(server->err_info, server->listen_fd);  /*设置非阻塞*/
    }

    /*将 listen_fd 注册到epoll的实例上,事件处理函数为acceptTcpHandler*/
    if (aeCreateFileEvent(server->loop, server->listen_fd, AE_READABLE, acceptTcpHandler, server) != AE_ERR) {
        char conn_info[64];
        anetFormatSock(server->listen_fd, conn_info, sizeof(conn_info));
        printf("listen on: %s\n", conn_info);
    }
}

三、wait_server 开始进入loop循环

void wait_server(server_t *server)
{
    aeMain(server->loop);     //是一个while循环,不断循环处理
    aeDeleteEventLoop(server->loop); //如果出了循环,就删除loop
}


void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;                         //设置停止标志为0,表示不停止
    while (!eventLoop->stop) {                   //如果没有被设置为1
        if (eventLoop->beforesleep) {
            eventLoop->beforesleep(eventLoop);
        }
        aeProcessEvents(eventLoop, AE_ALL_EVENTS); //整个事件处理核心函数,实际上就再不断轮询这个函数
    }
}

至此,我们的服务器算是启动起来了,它目前完成的是将listen_fd注册到了epoll的结构上,下次如果有连接请求我们就可以处理了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨博东的博客

请我喝瓶可乐鼓励下~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值