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