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 <vector>
#include <functional>
#include <condition_variable>
#include <list>
#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<Socket, const char*> ClientMessageTuple;
typedef std::function<void(const ClientMessageTuple &)> 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<Socket> 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 <iostream>
#include <algorithm>
#include <tuple>
#include <cstring>
#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 <<std::endl;
continue;
}
for( int num = 0; num < numWaitingEvent; num ++)
{
if(events[num].data.fd == serverSocket) //client connect
{
clientSocket = accept(serverSocket, (struct sockaddr*)&clientSocketAddr, &clientSocketLen);
if(clientSocket < 0)
{
hand_error("accept");
}
std::cout<<"one client has connected!"<<std::endl;
vclients.push_back(clientSocket);
setNoBlock(clientSocket); //设置为非阻塞模式
event.data.fd = clientSocket;// 将新连接也加入EPOLL的监听队列
event.events = EPOLLIN | EPOLLET ;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, clientSocket, &event)< 0)
hand_error("epoll_ctl");
}
else if( events[num].events & EPOLLIN)
{
clientSocket = events[num].data.fd;
if(clientSocket < 0)
hand_error("cli_sock");
char recvBuffer[RECEIVE_DATA_MAX];
memset(recvBuffer, 0, sizeof(recvBuffer));
int num = read(clientSocket, recvBuffer, sizeof(recvBuffer));
if(num == -1)
{
hand_error("read have some problem:");
}
else if(num == 0) //stand of client have exit
{
std::cout<<"one client has exit!"<<std::endl;
close(clientSocket);
eventWillRemoving = events[num];
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientSocket, &eventWillRemoving);
vclients.erase(remove(vclients.begin(), vclients.end(),
clientSocket),vclients.end());
}
else
{
//核心代码,将客户端socket和接收到的数据打包成一个任务,放到任务队列里
auto tuple = std::make_tuple(clientSocket, recvBuffer);
threadPool.append(std::bind(epollInEvent, tuple));
}
}
}
}
}
//设置非阻塞模式
int GzhEpoll::setNoBlock(Socket sock)
{
int ret = fcntl(sock, F_SETFL, O_NONBLOCK );
if(ret < 0)
hand_error("setnoblock");
return 0;
}
//设置阻塞模式
int GzhEpoll::setBlock(Socket sock)
{
int ret = fcntl(sock, F_SETFL, 0);
if (ret < 0 )
hand_error("setblock");
return 0;
}
GzhEpoll::GzhEpoll()
{
}
GzhEpoll::~GzhEpoll()
{
close(epoll_fd);
}
server.h #ifndef _SERNASOCK_H_
#define _SERNASOCK_H_
#include <string.h>
#include <string>
#include <arpa/inet.h>
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h>
#include <unistd.h>
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 <iostream>
#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!!!" <<std::endl;
//端口复用
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(const void *)&opt, sizeof(opt));
//服务器地址
struct sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(port);
if(bind(sock, (struct sockaddr*)&serAddr, sizeof(serAddr)) < 0)
printf("bind error\n");
}
//监听
int CServerNativeSock::listen(int num) const
{
if(::listen(sock, num) < 0)
{
printf("listen error\n");
return -1;
}
return 0;
}
//接收客户端连接
int CServerNativeSock::accept(struct sockaddr *addr, socklen_t *len) const
{
int cli_sock = ::accept(sock, addr, len);
if(cli_sock < 0)
{
printf("accept error!!!\n");
return -1;
}
return cli_sock;
}
//发送数据
int CServerNativeSock::send(int cli_sock, const char *szBuffer, size_t len) const
{
return ::send(cli_sock, szBuffer, len, 0);
}
//接收数据
int CServerNativeSock::recv(int cli_sock, char *szBuffer, size_t len) const
{
return ::read(cli_sock, szBuffer, len);
}
测试主函数
test.cpp
#include <iostream>
#include <cstring>
#include <tuple>
#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 <thread>
#include <mutex>
#include <condition_variable>
#include <list>
#include <vector>
#include <memory>
#include <functional>
namespace netlib
{
class ThreadPool
{
public:
typedef std::function<void(void)> 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<Task> tasks_;
//线程池是否在运行
bool running_;
//线程数
int threadNumber_;
//用来保存线程对象指针
std::vector<std::shared_ptr<std::thread>> threads_;
};
}
#endif
ThreadPool.cpp
#include "ThreadPool.h"
#include <stdio.h>
#include <thread>
#include <mutex>
#include <memory>
#include <functional>
#include <unistd.h>
#include <iostream>
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::thread>(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<std::mutex> guard(mutex_);
tasks_.push_front(task); //将该任务加入任务队列
condition_empty_.notify_one();//唤醒某个线程来执行此任务
return true;
}
void ThreadPool::threadWork(void)
{
Task task = NULL;
while(running_)
{
{
std::lock_guard<std::mutex> 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 <sys/types.h>
#include <sys/epoll.h> //epoll ways file
#include <sys/socket.h>
#include <fcntl.h> //block and noblock
#include <error.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#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
来源:CSDN
作者:lengyue_wuxin
链接:https://blog.csdn.net/lengyue_wuxin/article/details/78779739