小白写"半同步半异步"服务器模型踩坑实录(1)

我想按照自己的理解写一个”半同步半异步”的服务器模型,和常规的模式相同,分为三层:

最上层:使用epoll监控所有的socketfd,如果某个fd发生IO事件就将其加入中间层的任务队列。
中间层:任务队列,它里面保存发生事件的epoll_fd。
最底层:线程池,如果任务队列中有fd,就用一个线程处理。

这个模型非常简单,半同步半异步的含义是:

  • 半同步指的是在最上层和中间层之间是同步的,如果有连接事件或者IO读写事件,马上能将其添加到任务队列中。
  • 半异步指的是将任务添加到任务队列中并不一定马上处理,或者说有可能IO操作非常耗时,不一定能马上完成。

于是我满怀激情画出了下面的图:
这里写图片描述

接着我开始实施了,我打算用c++写,先完成第一层和第二层:

首先我定义了server.h

#ifndef _SERVER_H
#define _SERVER_H
#include<queue>
#include<sys/epoll.h>
#include<netinet/in.h>
#include"thpoll.h"

#define BACKLOG 1024                         //listen 的第二个参数 backlog
#define EPOLL_SIZE 1024                      //epoll_create 向内核中注册的兴趣事件的个数,虽然在内核2.6之后没用
#define SERV_PORT 8888

class server {
public:
    server() = default;
    void init(thpoll &);
    void run(thpoll &);
    void thread_func(thpoll &);

private:
    int  listen_fd;                           //socket返回
    int  epoll_fd;                            //epoll_create 返回
    int  conn_fd;                             //accept返回
    int  nfds;                                //epoll_wait 返回
    int  popfd;                               //从消息队列中pop出fd
    socklen_t  conn_len;
    struct sockaddr_in serv_addr,conn_addr;
    struct epoll_event ev, evs[EPOLL_SIZE];

    std::queue<int> MessageQueue;

};
#endif

1:错误一:server = default= default表示默认的构造方法,这是在c++ 11中新的写法。但是需要这样写server() = default,注意:括号不能少

默认构造函数:如果我们定义一个类,没有写任何的构造函数,则编译器大多数情况下会默认为我们合成一个构造函数,如果我们在定义变量时类内初始化了,则相应变量的值就是我们初始化的值。如果我们需要定义别的带参数的构造函数,那么定义默认构造函数的意义在于:我们在定义带参数的构造函数情况下还需要一个不带参数的构造函数。

然后继续server.cpp,这时候我想起来有关函数错误的处理,自己写个error类吧,也方便管理,于是我写了这样的error.h

#ifndef _ERROR_H
#define _ERROR_H

#include<errno.h>

class error {
public:

    error() = default;

    error(const std::string errmsg,int errline) {
        msg = errmsg;
        line = errline;
    }

    void show() {
        std::cerr << line << " : ";
        perror(msg.c_str());
    }

    void set(const std::string errmsg, const int errline) {
        msg = errmsg;
        line = errline;
    }
private:
    std::string msg = "";
    int  line = 0;

};
#endif

到了server.cpp中处理错误的时候::

    if((listen_fd = socket(AF_INET,SOCK_STREAM,0)) == -1) {
        error myerror().set("socket",__LINE__).show();
    }

错误一大把,谁让之前java写的太随意,现在终于吃到了苦果:

2:错误二:error myerror(); 这是返回值为error,并且不带参数的函数声明,如果想定义一个默认初始化的对象,应该这样:error myerror;

3:错误三:.set("socket",__LINE__).show();set中没有任何返回,却又继续调用了.show 方法。要想这样使用必须让set方法返回*this

如果我们需要类成员函数的一个方法返回这个对象本身然后接着下面调用另一个方法,返回值必须是这个对象的引用,不然只是拷贝了这个对象,以后失效。比如.move()方法将对象移动到某个位置,.set()方法设置对象移动到某个位置之后的动作,如果这样使用.move().set(),那我们应该这么声明move

类名 &move() { 
    return *this;
}

终于改好了这个error类,接下来完成了epoll部分的代码,可是我突然发现,我的任务队列应该怎么才能知道其中有任务呢,最后想,必须用一个线程监控它的情况才可以…,还好c++11std::thread,于是server.cpp中加入了run()函数,它是这样的:

void server::run(thpoll& mythpoll) 
{
    server myserver;
    std::thread t(&server::thread_func,&myserver,mythpoll);
    t.detach();

}

void server::thread_func(thpoll& mythpoll)
{
    while(1) {
        if(MessageQueue.empty() == false) {  // 如果队列中有东西
            std::cout << "拿出了 fd : " << MessageQueue.front() << std::endl;
            MessageQueue.pop();
        }
        sleep(1);
    }
}

线程一旦启动就执行thread_func函数,while(1)循环检测MessageQueue是否为空,如果不为空就pop出来一个fd,然后我计划是把fd在做处理。接着做了测试,发现如果服务器有连接,会把fd加入到队列中,然后立马pop出来。我觉得自己离成功又进了一步。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杨博东的博客

请我喝瓶可乐鼓励下~

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

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

打赏作者

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

抵扣说明:

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

余额充值