一个超级简单的服务器框架

    1. 本服务器端框架采用epoll+线程池+任务队列

    2. Epoll和sernasock是我封装的,ThreadPool用的是

        http://blog.csdn.net/shreck66/article/details/50412986

    Epoll.h

/**
 * by:gzh
 * 2017.12.12
 * 可以改进的地方:EPOLLIN 接收数据read()处可以优化,
 *     现在每次可以接收1kb的数据
 */
#ifndef _GZH_EPOLL_H_
#define _GZH_EPOLL_H_

#include 
#include 
#include 
#include 
#include "net.h"

namespace gzhlib
{

class GzhEpoll
{
public:
    typedef int Socket;
    typedef struct epoll_event EpollEvent;
    typedef struct sockaddr_in SocketAddr;
    typedef socklen_t SockLen;
    typedef std::tuple ClientMessageTuple;
    typedef std::function Callback;
    
    //核心代码,服务器接收到数据,在此回调函数中处理
    Callback epollInEvent;

private:
    const static int RECEIVE_DATA_MAX = 1024;

    //默认最大epoll监听事件数
    const static int EVENTS_MAX = 50000;
    const static int EPOLL_TIME_OUT = -1;

    //epoll fd
    int epoll_fd;
    
    //线程池中线程的数目
    int numbersOfThread;
    
    //server fd
    Socket serverSocket; 

    //client fd
    Socket clientSocket;

    //save client fd;
    //该数组暂时用不到
    std::vector vclients;

    //自定义的处理的最大事件数
    int maxNumEvents;

    //be removing event
    EpollEvent eventWillRemoving;

    EpollEvent event;
    
    //EpollEvent *events; 
    EpollEvent events[EVENTS_MAX];

    //用来 save client addr
    SocketAddr clientSocketAddr;   
    
    SockLen clientSocketLen;

public:
    //通过该函数创建epoll对象
    static GzhEpoll* create(Socket serverSocket, int maxEvents, int threadCounts);

    //禁用拷贝构造函数和赋值操作符
    GzhEpoll(const GzhEpoll &) = delete;
    GzhEpoll& operator=(const GzhEpoll &) = delete;

    //epoll主循环
    void run();
    
    ~GzhEpoll();

private:
    int init(Socket serverSocket, int maxEvents, int threadCounts);

    GzhEpoll();
    
    //设置非阻塞模式
    int setNoBlock(Socket sock);  

    //设置阻塞模式
    int setBlock(Socket sock);
};

}

#endif //_GZH_EPOLL_H_

    Epoll.cpp

#include 
#include 
#include 
#include 
#include "Epoll.h"
#include "ThreadPool.h"

using namespace gzhlib;

GzhEpoll* GzhEpoll::create(Socket serverSocket, int maxEvents, int threadCounts)
{
    auto pRet = new GzhEpoll();
    pRet->init(serverSocket, maxEvents, threadCounts);
    return pRet;
}

int GzhEpoll::init(Socket serverSocket, int maxEvents, int threadCounts)
{
    this->serverSocket = serverSocket;
    this->maxNumEvents = maxEvents;
    this->numbersOfThread = threadCounts;

    //设置非阻塞
    setNoBlock(serverSocket);
    
    return true;
}

//主循环,对epoll的更多了解,请百度  
void GzhEpoll::run()
{
    //开启线程池
    netlib::ThreadPool threadPool(numbersOfThread);
    threadPool.start();

    //等待处理事件的数量
    int numWaitingEvent = 0;

    event.events = EPOLLET | EPOLLIN;   //边缘方式触发
    event.data.fd = serverSocket;

    epoll_fd = epoll_create(EPOLL_CLOEXEC);   //create epoll,返回值为epoll的文件描述符
    if(epoll_fd < 0)
        hand_error("epoll_create");
    int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, serverSocket, &event);   //添加时间
    if(ret < 0)
        hand_error("epoll_ctl");

    while(true)
    {
        numWaitingEvent = epoll_wait(epoll_fd, events, maxNumEvents-1, EPOLL_TIME_OUT); 
        if(numWaitingEvent == -1)
        {
            hand_error("epoll_wait");
        }
        if(numWaitingEvent == 0)
        {
            std::cout<<"numWaitingEvent = "<< numWaitingEvent <
    server.h  
#ifndef _SERNASOCK_H_
#define _SERNASOCK_H_

#include 
#include 
#include 
#include     // for sockaddr_in
#include     // for socket
#include  
#include 

namespace gzhlib
{

class CServerNativeSock
{
private:
    int port;
    int sock;

private:
    void init();

public:
    CServerNativeSock(uint16_t port);

    ~CServerNativeSock(){close(sock);}

public:
    
    //获取端口
    uint16_t getPort() {return this->port;};
    
    //得到描述符
    int getSock() {return sock;};
    
    //监听
    int listen(int num) const;
    
    //接收客户端连接
    int accept(struct sockaddr *addr, socklen_t *len) const;
    
    //发送数据
    int send(int cli_sock, const char *szBuffer, size_t len) const;
    
    //接收数据
    int recv(int cli_sock, char *szBuffer, size_t len) const;
    
};
}
#endif //_SERNASOCK_H_
    server.cpp
/**
 * CServerNativeSock实现
 */
#include 
#include "sernasock.h"

using namespace gzhlib;

//构造函数
CServerNativeSock::CServerNativeSock(uint16_t port):port(port)
{
    init();
}

//端口复用,绑定地址
void CServerNativeSock::init()
{
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
		std::cout<< "server socket create error!!!" <

    测试主函数

    test.cpp

#include 
#include 
#include 
#include "Epoll.h"
#include "server.h"
using gzhlib::GzhEpoll;

int main()
{
    gzhlib::CServerNativeSock server(8889);
    server.listen(5);
    
    //epoll最大监听1000个事件,开启4个线程
    auto epoll = gzhlib::GzhEpoll::create(server.getSock(), 1000, 4);
    
    //此处处理接收到的数据
    epoll->epollInEvent = [](const GzhEpoll::ClientMessageTuple &clientMessage){
        GzhEpoll::Socket sock = std::get<0>(clientMessage);
        const char *c = std::get<1>(clientMessage);
        write(sock, c, strlen(c));
    };

    //开启主循环
    epoll->run();
    return 0;
}
    接下来是线程池,详情请移步 http://blog.csdn.net/shreck66/article/details/50412986

    这里只贴代码,不做解释。

    ThreadPool.h

#ifndef THREAD_POOL_H_
#define THREAD_POOL_H_

#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace netlib
{
    class ThreadPool
    {
        public:
            typedef std::function Task;
            ThreadPool(int threadNumber);
            ~ThreadPool();

            //往任务队列里添加任务
            bool append(Task task);

            //启动线程池
            bool start(void);

            //停止线程池
            bool stop(void);

        private:
            //线程所执行的工作函数
            void threadWork(void);    

            //互斥锁
            std::mutex mutex_;                                              
            //当任务队列为空时的条件变量
            std::condition_variable_any condition_empty_;                   
            //任务队列
            std::list tasks_;                                         
            //线程池是否在运行
            bool running_;                                                  
            //线程数
            int threadNumber_;                                              
            //用来保存线程对象指针
            std::vector> threads_;             
    };
}

#endif
    ThreadPool.cpp

#include "ThreadPool.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace netlib;

ThreadPool::ThreadPool(int threadNumber)
    :threadNumber_(threadNumber),
    running_(true)
{
}

ThreadPool::~ThreadPool()
{
    if(running_)
    {
        stop();
    }
}

bool ThreadPool::start(void)
{
    for(int i = 0; i < threadNumber_; i++)
    {
        auto t = std::make_shared(std::bind(&ThreadPool::threadWork,this));
        threads_.push_back(t);//循环创建线程      
    }

    usleep(500);
    //printf("线程池开始运行\n");
    return true;   
}

bool ThreadPool::stop(void)
{
    if(running_)
    {
        running_= false;
        for(auto t : threads_)
        {
            t->join();  //循环等待线程终止
        }
    }
    return true;
}

bool ThreadPool::append(Task task)
{

    std::lock_guard guard(mutex_);
    tasks_.push_front(task);   //将该任务加入任务队列
    condition_empty_.notify_one();//唤醒某个线程来执行此任务
    return true;
}

void ThreadPool::threadWork(void)
{
    Task task = NULL;
    while(running_)
    {   
        {
            std::lock_guard guard(mutex_);
            if(tasks_.empty())
            {
                condition_empty_.wait(mutex_);  //等待有任务到来被唤醒
            }
            if(!tasks_.empty())
            {
                task = tasks_.front();  //从任务队列中获取最开始任务
                tasks_.pop_front();     //将取走的任务弹出任务队列
            }
            else
            {
                continue;
            }
        }
        task(); //执行任务
    }
}

    net.h

/***********
net.h
***********/

#ifndef _NET_H
#define _NET_H

#include 
#include   //epoll ways file
#include 
#include     //block and noblock

#include 
#include 
#include 
#include 

#include 

#define hand_error(msg) do{perror(msg); exit(EXIT_FAILURE);}while(0)
#endif


CMakeLists.txt

#1.cmake verson,指定cmake版本 
cmake_minimum_required(VERSION 3.2)

#2.project name,指定项目的名称,一般和项目的文件夹名称对应
#可执行文件的名称
PROJECT(server)

#3.头文件在当前目录,可以不用写
INCLUDE_DIRECTORIES(

)

#4.将需要的源文件放到一个变量里如:SOURCE_FILE
SET(SOURCE_FILE
    Epoll.cpp
    server.cpp
    ThreadPool.cpp
    test.cpp
)

#6.add executable file,添加要编译的可执行文件
ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCE_FILE})

#7.add link library,添加可执行文件所需要的库,比如我们用到了libm.so(命名规则:lib+name+.so),就添加该库的名称
SET(SHARED_LIB
    pthread
)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${SHARED_LIB})

    编译步骤:

            mkdir build

            cd build

            cmake ..

            make

    运行:

            ./server



 

你可能感兴趣的:(c++)