C/C++开发,利用epoll创建自己的后台服务,实现对各个客户端网络通信(含示例代码)

目录

一、epoll API

二、方案设计

三、代码案例

四、编译测试

五、附件


        在前文“c/c++如何实现根据磁盘空间管理自己的日志文件案例_py_free的博客-CSDN博客”中,日志是以文件存储的方式进行本地化存储的,对于单机的本地系统而言,是可行的,但对于多机部署的分布式系统,那么需要将日志信息汇聚到后台,由日志综合管理系统统一提供日志汇总、管理、分析等服务能力。

一、epoll API

        日志传输方法有很多,假设现成的ftp服务、中间服务、socket通信、rpc通信、HTTP通信等,本文将介绍如何利用socket通信中的epoll相关API搭建一个简要的通信服务端,接收来自各个客户端的推送信息。

        epoll的理论略过,简要说一下epoll的接口,就三个API函数:
        1.、int epoll_create(int size);
        创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,记得用完epoll后,调用close()关闭。
        2.、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

        epoll的事件注册函数,epoll_ctl向 epoll对象中添加、修改或者删除感兴趣的事件,返回0表示成功,否则返回–1,此时需要根据errno错误码判断错误类型。

        参数说明:

        1)epfd是epoll_create()的返回值;

        2)op表示动作,用三个宏来表示:EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;
        3)fd是需要监听的fd;

        4)event是告诉内核需要监听什么事,struct epoll_event结构如下:

        events可以是以下几个宏的集合:
        1)EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
        2)EPOLLOUT:表示对应的文件描述符可以写;
        3)EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
        4)EPOLLERR:表示对应的文件描述符发生错误;
        5)EPOLLHUP:表示对应的文件描述符被挂断;
        6)EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
        7)EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

        3、int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
        等待事件的产生,函数返回需要处理的事件数目,如返回0表示已超时。如果返回–1,则表示出现错误,需要检查 errno错误码判断错误类型。epoll_wait方法返回的事件必然是通过 epoll_ctl添加到 epoll中的。

        参数说明:

        1) epfd是 epoll的描述符。

        2) events则是分配好的 epoll_event结构体数组,epoll将会把发生的事件复制到 events数组中。

        3) maxevents表示本次可以返回的最大事件数目。

        4) timeout表示在没有检测到事件发生时最多等待的时间(单位为毫秒),如果 timeout为0,则表示 epoll_wait在 rdllist链表中为空,立刻返回,不会等待。

二、方案设计

        本文中设计主要思路是,相比直接采用socket实现通信,采用epoll API能力来管理socket事务处理:

        【1】创建一个epoll线程类,即基于pthread.h提供线程能力结合epoll API的线程类

        【2】该线程类提供socket套接字打开、绑定、侦听能力,以及创建一个epoll句柄,并将套接字句柄添加到epoll,实现socket事件触发等待。

        【3】事件被触发后,根据描述符的类型和事件类型进行处理,实现socket事件的连接、删除、读取、写入等操作。

        【4】加入读取消息、写入消息的基于deque和互斥锁构建缓存队列进行消息缓存,确保关于socket事务可以有效处置。

        即epoll线程类包含线程能力、epoll API能力及消息缓存队列能力。

三、代码案例

        代码目录设计如下:

epoll_test
    bin
    src
        epoll_socket.h
        epoll_socket.cpp
        Mutex.h
        Mutex.cpp
        queuedata.h
        thread_py.h
        thread_py.cpp
        main.cpp
    Makefile

        epoll_socket.h,对外提供获取信息get及发送信息send,其实就是从缓存队列获取或添加消息,真实的消息接收及发送消息在其内部依据事务触发实现,读者可以依据自身项目需要调整。

#ifndef _EPOLL_SOCKET_H_
#define _EPOLL_SOCKET_H_
/**********************************************************************************
  *Copyright 2020-09-21, pyfree
  *
  *File Name       : epoll_socket.h
  *File Mark       : 
  *Summary         : 
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ***********************************************************************************/
#include <sys/socket.h>  
#include <sys/epoll.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <string>
#include <set>

#include "thread_py.h"
#include "queuedata.h"
#include "Mutex.h"

struct ItemCache
{
	ItemCache()
		: data(""),fd(-1)
	{
	};
	ItemCache(std::string data_)
		: data(data_),fd(-1)
	{
	};
	ItemCache(std::string data_,int fd_)
		: data(data_),fd(fd_)
	{
	};
	std::string data;
	int fd;
};

class epoll_socket : public Thread_py
{
public:
	epoll_socket();
	~epoll_socket();
	/**
	 * 打开服务侦听
	 * @param port {int} 侦听端口
	 * @param isblock_listen {bool} 是否阻塞
	 * @return {void} 
	 */
	bool open(int port, bool isblock_listen = false);

	int get_socketfd();
	/**
	 * 线程运行函数
	 * @return {char*} 
	 */
	int run();
	/**
	 * 指定fd发送字符串
	 * @param item {string} 字符内容
	 * @param fd {int} -1时,对所有fd发送
	 * @return {void} 
	 */
	void send(std::string item, int fd=-1);
	/**
	 * 获取客户端内容
	 * @param item {string&} 字符内容
	 * @param fd {int&} 	客户端fd
	 * @return {bool} 
	 */
	bool get(std::string &item,int &fd);
	/**
	 * 获取最新异常信息
	 * @return {char*} 
	 */
	char* get_errmsg();
	/**
	 * 设置读写数据时是否打印输出
	 * @param flag {bool} 	是否打印
	 * @return {char*} 
	 */
	void setPrintFlag(bool flag);
	/**
	 * 设置客户端事务是否阻塞
	 * @param flag {bool} 	是否阻塞
	 * @return {char*} 
	 */
	void setClientBlock(bool flag);
private:
	void add_event(int fd, int state);
	void del_event(int fd, int state);
	void mod_event(int fd, int state);
	void add_client(int fd);
	void del_client(int fd);

	void handle_events(struct epoll_event *events, int num);

	bool handle_accept();

	bool do_read(int fd);

	bool do_write(int fd);
private:
	bool running;
	bool print_flag;
	bool isblock_client;
	int socketfd;
	struct sockaddr_in servaddr;
	int epollfd;
	char err_msg[256];
	std::set<int> fds_client;
	PYMutex	fds_mutex;
	QueueData<ItemCache> buffer_read;
	QueueData<ItemCache> buffer_write;
};

#endif

        epoll_socket.cpp

#include "epoll_socket.h"

#include <fcntl.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <string.h>
#include <errno.h>  
#include <iostream> 

#define MAXBUFFSIZE	1024
#define MAXEVENTS	500
#define FDSIZE		1000

epoll_socket::epoll_socket() 
	: running(true)
	, print_flag(false)
	, isblock_client(false)
{
	socketfd = 0;
	memset(&servaddr, 0, sizeof(servaddr));
}

epoll_socket::~epoll_socket()
{
	running = false;
}

bool epoll_socket::open(int port, bool isblock_listen)
{
	if ((socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
		printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
		return false;
	}
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
	servaddr.sin_port = htons(port);
	if (!isblock_listen) {
		int flags = fcntl(socketfd, F_GETFL, 0);
		fcntl(socketfd, F_SETFL, flags | O_NONBLOCK);//设置为非阻塞
	}

	//设置重用地址,防止Address already in use
	int on = 1;
	if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){
		snprintf(err_msg, sizeof(err_msg)
			, "set reuse addr error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}

	//将本地地址绑定到所创建的套接字上  
	if (bind(socketfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
		snprintf(err_msg, sizeof(err_msg)
			, "bind socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}
	//开始监听是否有客户端连接  
	if (listen(socketfd, 5) == -1) {
		snprintf(err_msg, sizeof(err_msg)
			, "listen socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}
	std::cout << "create socket success\n";
	return true;
}

int epoll_socket::get_socketfd()
{
	return socketfd;
}

int epoll_socket::run()
{
	//创建一个描述符
	if ((epollfd = epoll_create(FDSIZE)) == -1){
		snprintf(err_msg, sizeof(err_msg)
			, "listen socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return -1;
	}
	//添加监听描述符事件
	add_event(socketfd, EPOLLIN);
	struct epoll_event events[MAXEVENTS];
	int ret;
	while (running) {
		//获取已经准备好的描述符事件
		ret = epoll_wait(epollfd, events, MAXEVENTS, 1);
		handle_events(events, ret);
	}
	close(epollfd);
	return 0;
}

void epoll_socket::send(std::string item,int fd/*=-1*/)
{
	if(fd<0){
		fds_mutex.Lock();
		std::set<int> fds_ = fds_client;
		fds_mutex.Unlock();
		std::set<int>::iterator it = fds_.begin();
		for (; it!=fds_.end(); ++it)
		{
			buffer_write.add(ItemCache(item,fd));
			mod_event(*it,EPOLLOUT);
		}
	}else{
		fds_mutex.Lock();
		bool f_ = (fds_client.find(fd)!=fds_client.end());
		fds_mutex.Unlock();
		if(f_)
		{
			buffer_write.add(ItemCache(item,fd));
			mod_event(fd,EPOLLOUT);
		}
	}
}

bool epoll_socket::get(std::string &item,int &fd)
{
	bool ret =false;
	ItemCache it;
	if( buffer_read.pop(it))
	{
		item = it.data;
		fd = it.fd;
		ret = true;
	}
	return ret;
}

void epoll_socket::add_event(int fd, int state)
{
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	/*
	//如果是ET模式,设置EPOLLET
	ev.events |= EPOLLET;
	*/
	//设置是否阻塞
	if(!isblock_client){
		int flags = fcntl(fd, F_GETFL);
		fcntl(fd, F_SETFL, flags | O_NONBLOCK);
	}
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}

void epoll_socket::del_event(int fd, int state)
{
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
}

void epoll_socket::mod_event(int fd, int state)
{
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);
}

void epoll_socket::add_client(int fd)
{
	fds_mutex.Lock();
	fds_client.insert(fd);
	fds_mutex.Unlock();
}
void epoll_socket::del_client(int fd)
{
	fds_mutex.Lock();
	fds_client.erase(fd);
	fds_mutex.Unlock();
}

void epoll_socket::handle_events(epoll_event * events, int num)
{
	int i;
	int fd;
	//进行选好遍历
	for (i = 0; i < num; i++) {
		fd = events[i].data.fd;
		//根据描述符的类型和事件类型进行处理
		if ((fd == socketfd) && (events[i].events& EPOLLIN)){
			handle_accept();
		}else if (events[i].events & EPOLLIN){
			do_read(fd);
		}else if (events[i].events & EPOLLOUT){
			do_write(fd);
		}else{
			del_client(fd);
			close(fd);
		}
	}
}

bool epoll_socket::handle_accept()
{
	int clifd;
	struct sockaddr_in cliaddr;
	socklen_t cliaddrlen = sizeof(cliaddr);
	clifd = accept(socketfd, (struct sockaddr*)&cliaddr, &cliaddrlen);
	if (clifd == -1) {
		snprintf(err_msg, sizeof(err_msg)
			, "listen socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}
	else {
		char msg[128] = { 0 };
		//获取端口错误
		sprintf(msg,"accept a new client(%d):%s:%d\n"
			, clifd, inet_ntoa(cliaddr.sin_addr)
			, cliaddr.sin_port);
		std::cout << msg;
		//添加一个客户描述符和事件
		add_event(clifd, EPOLLIN);
		add_client(clifd);
	}
}

bool epoll_socket::do_read(int fd)
{
	char buf[MAXBUFFSIZE]={0};
	int buflen = read(fd, buf, MAXBUFFSIZE);
	if (buflen == -1) {
		snprintf(err_msg, sizeof(err_msg)
			, "read error(%d): %s(errno: %d)\n"
			, fd, strerror(errno), errno);
		std::cout << err_msg;
		del_client(fd);
		close(fd);
		del_event(fd, EPOLLIN);
		return false;
	}
	else if (buflen == 0) {
		char msg[128] = { 0 };
		sprintf(msg,"client(%d) close.\n", fd);
		std::cout << msg;
		del_client(fd);
		close(fd);
		del_event(fd, EPOLLIN);
		return true;
	}
	else {
		if(print_flag){
			char msg[MAXBUFFSIZE] = { 0 };
			sprintf(msg, "read message is:%s\n", buf);
			std::cout << msg;
		}
		buffer_read.add(ItemCache(std::string(buf,buflen),fd));
	}
	return true;
}

bool epoll_socket::do_write(int fd)
{
	ItemCache it;
	if(!buffer_write.pop(it))
	{
		return false;
	}
	if(it.fd!=fd){
		snprintf(err_msg, sizeof(err_msg)
			, "write error,fd(%d,%d)\n",it.fd,fd);
		std::cout << err_msg;
	}
	int nwrite;
	nwrite = write(fd, it.data.c_str(), it.data.length());
	if (nwrite == -1)
	{
		snprintf(err_msg, sizeof(err_msg)
			, "write error: %s(errno: %d)\n"
			, strerror(errno), errno);
		std::cout << err_msg;
		del_client(fd);
		close(fd);
		del_event(fd, EPOLLOUT);
		return false;
	}
	else{ 
		if(print_flag){
			char msg[MAXBUFFSIZE] = { 0 };
			sprintf(msg, "write message is:%s\n", it.data.c_str());
			std::cout << msg;
		}
		mod_event(fd, EPOLLIN);
	}
	return true;
}

char * epoll_socket::get_errmsg()
{
	return err_msg;
}

void epoll_socket::setPrintFlag(bool flag)
{
	print_flag = flag;
}

void epoll_socket::setClientBlock(bool flag)
{
	isblock_client = flag;
}

        Mutex.h,简要的互斥锁实现

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _PYMUTEX_H_
#define _PYMUTEX_H_

/***********************************************************************
  *Copyright 2020-09-21, pyfree
  *
  *File Name       : Mutex.h
  *File Mark       : 
  *Summary         : 线程锁
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/

#ifdef WIN32
//#include <windows.h>
#else
#include <pthread.h>
#endif

typedef void *HANDLE;

class IMutex
{
public:
	virtual ~IMutex() {}

  /**
	 * 上锁
	 * @return {void} 
	 */
	virtual void Lock() const = 0;
  /**
	 * 尝试上锁
	 * @return {void} 
	 */
	virtual bool TryLock() const = 0;
  /**
	 * 解锁
	 * @return {void} 
	 */
	virtual void Unlock() const = 0;
};

class PYMutex : public IMutex
{
public:
	PYMutex();
	~PYMutex();

	virtual void Lock() const;
	virtual bool TryLock() const;
	virtual void Unlock() const;
private:
#ifdef _WIN32
	HANDLE m_mutex;
#else
	mutable pthread_mutex_t m_mutex;
#endif
};

#endif //_PYMUTEX_H_

        Mutex.cpp

#include "Mutex.h"

#ifdef WIN32
#include <windows.h>
#endif
//#include <iostream>
#include <stdio.h>

PYMutex::PYMutex()
{
#ifdef _WIN32
	m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#else
	pthread_mutex_init(&m_mutex, NULL);
#endif
}


PYMutex::~PYMutex()
{
#ifdef _WIN32
	::CloseHandle(m_mutex);
#else
	pthread_mutex_destroy(&m_mutex);
#endif
}


void PYMutex::Lock() const
{
#ifdef _WIN32
	WaitForSingleObject(m_mutex, INFINITE);
#else
	pthread_mutex_lock(&m_mutex);
#endif
}

bool PYMutex::TryLock() const
{
#ifdef _WIN32
    DWORD dwWaitResult = WaitForSingleObject(m_mutex, 0);  
	if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) {
		printf("thread WARNING: bad result from try-locking mutex\n");
	}
    return (dwWaitResult == WAIT_OBJECT_0) ? true : false; 
#else
	return (0==pthread_mutex_trylock(&m_mutex))?true:false;
#endif	
};

void PYMutex::Unlock() const
{
#ifdef _WIN32
	::ReleaseMutex(m_mutex);
#else
	pthread_mutex_unlock(&m_mutex);
#endif
}

        queuedata.h,消息缓存队列,添加了互斥锁确保跨线程使用安全,也确保逻辑顺序。同时提供一次获取多个数据接口

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _QUEUE_DATA_H_
#define _QUEUE_DATA_H_

/***********************************************************************
  *Copyright 2020-09-21, pyfree
  *
  *File Name       : queuedata.h
  *File Mark       : 
  *Summary         : 
  *数据队列类,线程安全
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#include <queue>
#include <deque>
#include <stdio.h>
#include <string.h>

#include "Mutex.h"

template <class T>
class QueueData
{
public:
	QueueData(std::string desc = "thread_queue");
	~QueueData();
	//
	/**
	 * 获取队列大小
	 * @return {int } 队列大小
	 */
	int size();
	/**
	 * 判定队列是否为空
	 * @return {bool } 是否为空队列
	 */
	bool isEmpty();
	/**
	 * 获取队列头元素
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool getFirst(T &it);
	/**
	 * 删除元素
	 * @return {bool } 是否成功
	 */
	bool removeFirst();
	/**
	 * 获取队列头元素,并从队列终删除
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool pop(T &it);
	/**
	 * 从队列头开始逐步获取多个元素,并剔除
	 * @param its {queue<T>&} 获取到的元素集
	 * @param sizel {int} 一次获取多少个
	 * @return {bool } 至少获取一个元素以上则成功
	 */
	bool getList(std::queue<T> &its,unsigned int sizel=5);
	/**
	 * 从队列尾部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add(T it);
	/**
	 * 从队列头部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add_front(T it);
	/**
	 * 清空元素
	 * @return {void }
	 */
	void clear();
private:
	void init();
	QueueData& operator=(const QueueData&) {return this;};
protected:
	std::string queue_desc;
private:
	/点集转发
	
	//协议解析结果缓存
	std::deque<T> datacache_queue;	//队列容器
	PYMutex m_Mutex;				//线程锁
	//
	static unsigned int QSize;		//队列大小约束,超出是会从队列头剔除旧数据腾出空位在对末添加数据
	//
	int queue_overS;				//队列溢出次数计数
};
template <class T>
unsigned int  QueueData<T>::QSize = 100;

template <class T>
QueueData<T>::QueueData(std::string desc)
	: queue_desc(desc)
{
	init();
};

template <class T>
void QueueData<T>::init() 
{
	queue_overS = 0;
};

template <class T>
QueueData<T>::~QueueData()
{

}

//
template <class T>
int QueueData<T>::size()
{
	int ret = 0;
	m_Mutex.Lock();
	ret = static_cast<int>(datacache_queue.size());
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::isEmpty()
{
	bool ret = false;
	m_Mutex.Lock();
	ret = datacache_queue.empty();
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::getFirst(T &it) 
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		it = datacache_queue.front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::removeFirst() 
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		datacache_queue.pop_front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::pop(T &it)
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		it = datacache_queue.front();
		datacache_queue.pop_front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
};

template <class T>
bool QueueData<T>::getList(std::queue<T> &its,unsigned int sizel)
{
	m_Mutex.Lock();
	while (!datacache_queue.empty())
	{
		its.push(datacache_queue.front());
		datacache_queue.pop_front();
		if (its.size() >= sizel)
		{
			break;
		}
	}
	m_Mutex.Unlock();
	return !its.empty();
};

template <class T>
void QueueData<T>::add(T it) 
{
	m_Mutex.Lock();
	if (datacache_queue.size() > QSize) 
	{
		queue_overS++;
		datacache_queue.pop_front();
	}
	datacache_queue.push_back(it);
	m_Mutex.Unlock();
	if (queue_overS >= 10) 
	{
		//每溢出10次,报告一次
		printf("add item to queue %s at end,but the size of QueueData is up to limmit size: %d .[%s %s %d]\n"
			, queue_desc.c_str(), QSize
			, __FILE__, __FUNCTION__, __LINE__);
		queue_overS = 0;
	}
}

template <class T>
void QueueData<T>::add_front(T it)
{
	m_Mutex.Lock();
	if (datacache_queue.size() > QSize) 
	{
		queue_overS++;
		datacache_queue.pop_front();
	}
	datacache_queue.push_front(it);
	m_Mutex.Unlock();
	if (queue_overS >= 10) 
	{
		//每溢出10次,报告一次
		printf("add item to queue %s at first,but the size of QueueData is up to limmit size: %d .[%s %s %d]\n"
			, queue_desc.c_str(), QSize
			, __FILE__, __FUNCTION__, __LINE__);
		queue_overS = 0;
	}
}

template <class T>
void QueueData<T>::clear()
{
	m_Mutex.Lock();
	datacache_queue.clear();
	m_Mutex.Unlock();
	queue_overS = 0;
}

#endif //_QUEUE_DATA_H_

        thread_py.h,线程类

#ifndef _THREAD_PY_H_
#define _THREAD_PY_H_
/**********************************************************************************
  *Copyright 2020-09-21, pyfree
  *
  *File Name       : thread_py.h
  *File Mark       : 
  *Summary         : 
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ***********************************************************************************/
#include <pthread.h>
#include <unistd.h>

class Thread_py
{
private:
    //current thread ID
    pthread_t tid;
    //thread status
    int threadStatus;
    //get manner pointer of execution 
    static void* run0(void* pVoid);
    //manner of execution inside
    void* run1();
public:
    //threadStatus-new create
    static const int THREAD_STATUS_NEW = 0;
    //threadStatus-running
    static const int THREAD_STATUS_RUNNING = 1;
    //threadStatus-end
    static const int THREAD_STATUS_EXIT = -1;
    // constructed function
    Thread_py();
    ~Thread_py();
    //the entity for thread running
    virtual int run()=0;
    //start thread
    bool start();
    //gte thread ID
    pthread_t getThreadID();
    //get thread status
    int getState();
    //wait for thread end
    void join();
    //wait for thread end in limit time
    void join(unsigned long millisTime);
};

#endif /* _Thread_py_H */

        thread_py.cpp

#include "thread_py.h"

#include <stdio.h>

void* Thread_py::run0(void* pVoid)
{
    Thread_py* p = (Thread_py*) pVoid;
    p->run1();
    return p;
}

void* Thread_py::run1()
{
    threadStatus = THREAD_STATUS_RUNNING;
    tid = pthread_self();
    run();
    threadStatus = THREAD_STATUS_EXIT;
    tid = 0;
    pthread_exit(NULL);
}

Thread_py::Thread_py()
{
    tid = 0;
    threadStatus = THREAD_STATUS_NEW;
}

Thread_py::~Thread_py()
{
	join(10);
}

int Thread_py::run()
{
    while(true){
        printf("thread is running!\n");
        sleep(100);
    }
    return 0;
}

bool Thread_py::start()
{
    return pthread_create(&tid, NULL, run0, this) == 0;
}

pthread_t Thread_py::getThreadID()
{
    return tid;
}

int Thread_py::getState()
{
    return threadStatus;
}

void Thread_py::join()
{
    if (tid > 0)
    {
        pthread_join(tid, NULL);
    }
}

void Thread_py::join(unsigned long millisTime)
{
    if (tid == 0)
    {
        return;
    }
    if (millisTime == 0)
    {
        join();
    }else
    {
        unsigned long k = 0;
        while (threadStatus != THREAD_STATUS_EXIT && k <= millisTime)
        {
            usleep(100);
            k++;
        }
    }
}
 

        main.cpp,创建一个epoll_socket实例,等待socket事件触发,从队列读取数据,并及时写入一个消息。

#pragma execution_character_set("utf-8")

#include <iostream>  
#include "epoll_socket.h"
using namespace std;

int main(int argc, char **argv)
{
	epoll_socket myepoll;
	if(!myepoll.open(5000,true)){
		cout << myepoll.get_errmsg();
	}
	myepoll.start();
    myepoll.setPrintFlag(true);
    std::string item;
    int fd = 0;
    int cout = 0;
    while (true)
    {
        if(myepoll.get(item,fd))
        {
            printf("read:%s\n",item.c_str());
            char buf[32]={0};
            sprintf(buf,"rec_count=%d\n",cout++);
            myepoll.send(std::string(buf),fd);
        }
        usleep(10);
    }
    
	return 0;
}

        Makefile,本人如果是纯粹的linux下c/c++编译时,常用的一个Makefile编译模板,推荐给读者,可以在该基础上扩展您们需要的工程描述,我曾用它管理过数十万有效代码的工程项目,Makefile文件也恒小,但轻松胜任。

ifeq ($(shell uname), Linux)

endif 
#PREFIXPATH 	:= /usr/bin
#CC 			:= $(PREFIXPATH)/gcc
#CX 			:= $(PREFIXPATH)/g++
CC 			:= gcc
CX 			:= g++
#COMPILEOPTION = -Wcast-align -Wno-deprecated -O2 -D_SBS7020B -finline-limit=3
#COMPILEOPTION := -Wcast-align -Wno-deprecated -g -finline-limit=3
LINKOPTION 	:= -Wl,--allow-multiple-definition
#export LD_LIBRARY_PATH=/usr/lib64
sysbit	:= x64
runType := release

TOPDIR 		:= .
BIN 		:= $(TOPDIR)/bin
ifeq ($(sysbit),x32)
CC	+= -m32
CX	+= -m32
endif
ifeq ($(runType),debug)
TARGET = $(BIN)/epolltestd.exe
else
TARGET = $(BIN)/epolltest.exe
endif
SRCDIR 		:= $(TOPDIR)/src

#INCLUDES
INCLUDEDIR 	:= -I"$(SRCDIR)"
#LIBDIRS 	:=
SHAREDLIB 	:= -lpthread -ldl -lrt 
SWQDll		:=
APPENDLIB 	:= 

ifeq ($(sysbit),x32)
APPENDDLLLIB +=
else
APPENDDLLLIB += -L"/lib64/"
endif
###############################################################################################################
%.o: %.c
	$(CC) $(LINKOPTION) $(INCLUDEDIR) -c $< -o $(BIN)/$(notdir $@)

%.o:%.cpp
	$(CX) $(LINKOPTION) $(INCLUDEDIR) -c $< -o $(BIN)/$(notdir $@)
###############################################################################################################
#DEBARSRC 指定需要剔除的源码
DEBARSRC := 
#SOURCES 可以在里面添加更多需要指定的目录及源码
SOURCES = $(filter-out $(DEBARSRC), $(wildcard $(SRCDIR)/*.c $(SRCDIR)/*.cpp))
OBJS = $(patsubst %.c,$(BIN)/%.o,$(patsubst %.cpp,$(BIN)/%.o,$(notdir $(SOURCES))))
OBJSS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))

$(TARGET) : $(OBJSS)
	$(CX) $(LINKOPTION) $(INCLUDEDIR) $(OBJS) -o $(TARGET) $(APPENDLIB) $(APPENDDLLLIB) $(SHAREDLIB) $(SWQDll)
	chmod a+x $(TARGET)

.PHONY: install
install:
	install -m 777 $(OBJS) $(INSTALLDIR)

.PHONY: clean
clean:
	rm -f *.*~ *~ $(OBJS) $(TARGET)

四、编译测试

        本文编译环境是win10安装了vmware,并在vmware安装了centos7系统,进入项目目录,cd epoll_test

[py@pyfree epoll_test]$ make
g++ -Wl,--allow-multiple-definition -I"./src" -c src/thread_py.cpp -o ./bin/thread_py.o
g++ -Wl,--allow-multiple-definition -I"./src" -c src/epoll_socket.cpp -o ./bin/epoll_socket.o
g++ -Wl,--allow-multiple-definition -I"./src" -c src/Mutex.cpp -o ./bin/Mutex.o
g++ -Wl,--allow-multiple-definition -I"./src" -c src/main.cpp -o ./bin/main.o
g++ -Wl,--allow-multiple-definition -I"./src" ./bin/thread_py.o ./bin/epoll_socket.o ./bin/Mutex.o ./bin/main.o -o ./bin/epolltest.exe  -L"/lib64/" -lpthread -ldl -lrt  
chmod a+x ./bin/epolltest.exe
[py@pyfree epoll_test]$ 

        测试,运行系统记得开放服务所需要的端口(5000),例如systemctl stop firewalld直接关闭防火墙,本文是采用telnet来测试:

五、附件

        上述已经提供完整代码及工程说明,如果还是无法组织其代码工程,可以去该链接下载:

https://download.csdn.net/download/py8105/86608655C

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

py_free-物联智能

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值