C++ epoll机制实现C/S架构服务端socket通信

##功能
1.支持1000+客户端链接
2.多线程处理:接收线程处理新连接,工作线程处理数据流动(可自行实现处理逻辑)
3.epoll机制
4.单例锁,互斥锁+条件变量

/*
 * myreactor.h
 *
 *  Created on: 2019-4-15
 *      Author: wlj
 */

#ifndef MYREACTOR_H_
#define MYREACTOR_H_
#include<string.h>
//#include<string>
#include<pthread.h>
#include<signal.h>
#include<list>
using namespace std;
#define WORKER_THREAD_NUM   5
class CMyReactor
{
public:
	static CMyReactor* GetInstance()
	{
		pthread_once(&m_once,InitInstance);
		return m_instance;
	}
	static void InitInstance()
	{
		m_instance = new CMyReactor();
	}


	~CMyReactor(void);
	/*
		功能:初始化
		入参:ip:IP地址,nport:端口号
		返回值:true/false
	 */
	bool Init(const char* ip, short nport);
	/*
	功能:循环epoll_wait()获取在线客户端个数,并判断ev[i].data.fd是链接线程还是工作线程,然后处理对应事件
		入参:无
		返回值:无
	 */
	void* MainLoop();


	/*
	功能:客户端退出
	入参:clientfd:客户端fd
	返回值:true/false
	 */
	bool DeleteClient(int clientfd);
	/*
	功能:服务端退出
	入参:none
	返回值:true/false
	 */
	bool ServiceExit();
protected:
	CMyReactor(void);
	CMyReactor(const CMyReactor&){};//禁止拷贝
	//	CMyReactor& operator=(const CMyReactor& reactor){return reactor;};//禁止赋值
	static CMyReactor* m_instance;
private:
	/*
		功能:socket创建监听套接字listenfd,epoll_create(),往epoll事件表g_epollfd中注册listenfd上的事件
		入参:ip:IP地址,nport:端口号
		返回值:true/false
	 */
	bool CreateServerListener(const char* ip, short port);

	/*
	功能:获取客户端套接字newfd,往epoll事件表g_epollfd中注册newfd上的事件入参:reactor:MyReactor对象
	返回值:none
	 */
	 class GarbageCollector {
    public:
        ~GarbageCollector() {
            if (CMyReactor::m_instance) {
                delete CMyReactor::m_instance;
                CMyReactor::m_instance = 0;
            }
        }
    };
	bool isIPAddressValid(const char* pszIPAddr);
	static GarbageCollector m_gc;          //静态变量,只是为了释放单例
	static void* AcceptThreadFunc(void *arg);
	static pthread_once_t m_once;
	int m_epollfd;
	bool m_bStop;// 服务器停止
	int m_listenfd;
	pthread_cond_t m_acceptCond;
	pthread_mutex_t m_acceptMutex;
	pthread_t m_acceptThreadid;
	pthread_t m_clientCheckid;

};

#endif /* MYREACTOR_H_ */

/*
 * myreactor.cpp
 *
 *  Created on: 2019-4-15
 *      Author: wlj
 */

#include"myreactor.h"
#include<sys/socket.h>
#include<stdlib.h>
#include <stdio.h>
#include<arpa/inet.h>
#include <netinet/in.h>
#include<sys/epoll.h>
#include<iostream>
#include <fcntl.h>
#include <pthread.h>
//#include <unistd.h>
//#include <sys/types.h>
#include <sstream>
#include <iomanip> //for std::setw()/setfill()
#include <semaphore.h>
#include <errno.h>
#include "jobthread.h"
#include "easylog.h"
#include "../msgprocess/clientheartcheck.h"
#define min(a, b) ((a <= b) ? (a) : (b))
#define EPNUM 1024
#define LINSTENNUM 50

pthread_once_t CMyReactor::m_once = PTHREAD_ONCE_INIT;
CMyReactor*CMyReactor::m_instance = NULL;
CMyReactor::GarbageCollector CMyReactor::m_gc;
CMyReactor::CMyReactor()
{
	m_listenfd = 0;
	m_epollfd = 0;
	m_acceptThreadid = 0;
	m_clientCheckid = 0;
	m_bStop = false;
}
CMyReactor::~CMyReactor()
{

}
bool CMyReactor::Init(const char* ip, short nport)
{
	//IP 端口号合法性判断
	if( !isIPAddressValid(ip))
	{
		LOG_ERROR("CMyReactor::Init::ip illegal!\n");
		return false;
	}
	if(nport >= 65535)
	{
		LOG_ERROR("CMyReactor::Init::port >= 65535!\n");
		return false;
	}
	LOG_INFO("CMyReactor::init:: begin! \n");
	if(!CreateServerListener(ip,nport))
	{
		LOG_ERROR("Unable to bind:%d,nport:%d\n",ip,nport);
		return false;
	}
	::pthread_cond_init(&m_acceptCond, NULL);
	::pthread_mutex_init(&m_acceptMutex, NULL);
	::pthread_create(&m_acceptThreadid,NULL,AcceptThreadFunc,NULL);

	


	LOG_INFO("CMyReactor::init:: end! \n");
	return true;
}
void*CMyReactor::MainLoop(void)
{
	LOG_INFO("CMyReactor::MainLoop:: begin! \n");
	LOG_INFO("main thread id = %u  \n", pthread_self());

	while(!m_bStop)
	{
		struct epoll_event ev[EPNUM];
		int epClientNum = ::epoll_wait(m_epollfd,ev,EPNUM,5);
		if(0 == epClientNum)
		{
			continue;
		}
		else if( 0 > epClientNum)
		{
			LOG_ERROR("epoll_wait error.\n");
			continue;
		}
		int minEpNum = min(epClientNum,EPNUM);
		LOG_INFO("minEpNum:%d\n",minEpNum);
		for(int i = 0; i< minEpNum; i++)
		{
			if(ev[i].data.fd == m_listenfd)
			{
				pthread_cond_signal(&m_acceptCond);
			}
			else
			{
				//处理接收到的数据
				//AddTaskToThreadPool(&(ev[i].data.fd));
			}
		}
	}
	LOG_INFO("CMyReactor::MainLoop:: end! \n");
	return NULL;
}
bool CMyReactor::DeleteClient(int clientfd)
{
	LOG_INFO("CMyReactor::DeleteClient begin \n");
	::pthread_mutex_lock(&m_acceptMutex);
	if (-1 == ::epoll_ctl( m_epollfd, EPOLL_CTL_DEL, clientfd, NULL) )
	{
		LOG_ERROR("CMyReactor::DeleteClient:: epoll_ctl EPOLL_CTL_DEL clientfd = %d failed\n",clientfd);
		::pthread_mutex_unlock(&  m_acceptMutex);
		return false;
	}
	::pthread_mutex_unlock(&  m_acceptMutex);
	LOG_INFO("CMyReactor::DeleteClient clientfd = %d \n",clientfd);
	LOG_INFO("CMyReactor::DeleteClient end \n");
	::close(clientfd);
	return true;
}
bool CMyReactor::ServiceExit()
{
	LOG_INFO("CMyReactor::ServiceExit::begin! \n");
	m_bStop = true;
	if(-1 == ::epoll_ctl(m_epollfd, EPOLL_CTL_DEL, m_listenfd, NULL))
	{
		LOG_ERROR("CMyReactor::ServiceExit:: m_listenfd exit fail!\n");
		return false;
	}
	::shutdown(m_listenfd, SHUT_RDWR);
	::close(m_listenfd);
	::close(m_epollfd);

	::pthread_cond_destroy(&m_acceptCond);
	::pthread_mutex_destroy(&m_acceptMutex);

	LOG_INFO("CMyReactor::ServiceExit::end! \n");
	return true;
}

bool CMyReactor::CreateServerListener(const char* ip, short port)
{
	LOG_INFO("CMyReactor::CreateServerListener::begin! \n");
	//IP合法性校验,端口号<65535
	if( !isIPAddressValid(ip))
	{
		LOG_ERROR("CMyReactor::CreateServerListener::ip illegal!\n");
		return false;
	}
	if(port >= 65535)
	{
		LOG_ERROR("CMyReactor::CreateServerListener::port >= 65535!\n");
		return false;
	}
	m_listenfd = ::socket(AF_INET,SOCK_STREAM | SOCK_NONBLOCK,0);

	if(-1 == m_listenfd)
	{
		LOG_ERROR("CMyReactor::CreateServerListener::socket fail!\n");
		return false;
	}

	LOG_INFO("m_listenfd =%d \n",m_listenfd);
	int iButtomOn = 1;
	::setsockopt(m_listenfd,SOL_SOCKET,SO_REUSEADDR,(char *)&iButtomOn,sizeof(iButtomOn));
	::setsockopt(m_listenfd,SOL_SOCKET,SO_REUSEPORT,(char *)&iButtomOn,sizeof(iButtomOn));

	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;//IPv4
	servaddr.sin_addr.s_addr = inet_addr(ip);
	servaddr.sin_port = htons(port);

	if(-1 == ::bind(m_listenfd,(sockaddr*)&servaddr,sizeof(servaddr)) )
	{
		LOG_ERROR("CMyReactor::CreateServerListener::bind fail!\n");
		return false;
	}

	if(-1 == ::listen(m_listenfd,LINSTENNUM))
	{
		LOG_ERROR("CMyReactor::CreateServerListener::listen fail!\n");
		return false;
	}
	m_epollfd = epoll_create(1);
	if(-1 == m_epollfd)
	{
		LOG_ERROR("CMyReactor::CreateServerListener::epoll_create fail!\n");
		return false;
	}

	struct epoll_event e;
	memset(&e,0,sizeof(e));
	e.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
	e.data.fd = m_listenfd;

	if(-1 == ::epoll_ctl(m_epollfd,EPOLL_CTL_ADD,m_listenfd,&e))
	{
		LOG_ERROR("CMyReactor::CreateServerListener::epoll_ctl EPOLL_CTL_ADD  m_listenfd = %d fail!\n",m_listenfd);
		return false;
	}

	LOG_INFO("epoll_ctl EPOLL_CTL_ADD  m_listenfd = %d success!\n",m_listenfd);
	LOG_INFO("CMyReactor::CreateServerListener::end! \n");
	return true;
}

void* CMyReactor::AcceptThreadFunc(void *arg)
{
	LOG_INFO("CMyReactor::AcceptThreadFunc thread_id = %d \n",m_instance->m_acceptThreadid);

	while(!m_instance-> m_bStop)
	{

		::pthread_mutex_lock(&m_instance->m_acceptMutex);
		::pthread_cond_wait(&m_instance->m_acceptCond,&m_instance-> m_acceptMutex);
		struct sockaddr_in clientaddr;
		socklen_t addrlen;
		int newClientfd = ::accept(m_instance->m_listenfd,(struct sockaddr*)&clientaddr,&addrlen);

		if(-1 == newClientfd)
		{
			LOG_ERROR("CMyReactor::AcceptThreadFunc::accept newClientfd fail\n");
			continue;
		}

		LOG_INFO("CMyReactor::AcceptThreadFunc new client = %d,connected:%s:%d \n",newClientfd,::inet_ntoa(clientaddr.sin_addr),::ntohs(clientaddr.sin_port));

		int oldflag = ::fcntl(newClientfd, F_GETFL,0);
		int newflag = oldflag | O_NONBLOCK;

		if(-1 == ::fcntl(newClientfd,F_SETFL,newflag))
		{
			LOG_ERROR("CMyReactor::AcceptThreadFunc::fcntl error, oldflag = %d,newflag = %d\n",oldflag,newflag);
			continue;
		}

		struct epoll_event e;
		memset(&e,0,sizeof(e));
		e.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
		e.data.fd = newClientfd;

		if(-1 == ::epoll_ctl(m_instance->m_epollfd, EPOLL_CTL_ADD, newClientfd, &e))
		{
			LOG_ERROR("CMyReactor::AcceptThreadFunc::epoll_ctl error, fd = %d\n",newClientfd);
		}
		::pthread_mutex_unlock(&m_instance->m_acceptMutex);
	}
	return NULL;
}

bool CMyReactor::isIPAddressValid(const char* pszIPAddr)
{
	if (!pszIPAddr) return false; //若pszIPAddr为空
	char IP1[100],cIP[4];
	int len = strlen(pszIPAddr);
	int i = 0,j=len-1;
	int k, m = 0,n=0,num=0;
	//去除首尾空格(取出从i-1到j+1之间的字符):
	while (pszIPAddr[i++] == ' ');
	while (pszIPAddr[j--] == ' ');

	for (k = i-1; k <= j+1; k++)
	{
		IP1[m++] = *(pszIPAddr + k);
	}
	IP1[m] = '\0';

	char *p = IP1;

	while (*p!= '\0')
	{
		if (*p == ' ' || *p<'0' || *p>'9') return false;
		cIP[n++] = *p; //保存每个子段的第一个字符,用于之后判断该子段是否为0开头

		int sum = 0;  //sum为每一子段的数值,应在0到255之间
		while (*p != '.'&&*p != '\0')
		{
			if (*p == ' ' || *p<'0' || *p>'9') return false;
			sum = sum * 10 + *p-48;  //每一子段字符串转化为整数
			p++;
		}
		if (*p == '.') {
			if ((*(p - 1) >= '0'&&*(p - 1) <= '9') && (*(p + 1) >= '0'&&*(p + 1) <= '9'))//判断"."前后是否有数字,若无,则为无效IP,如“1.1.127.”
				num++;  //记录“.”出现的次数,不能大于3
			else
				return false;
		};
		if ((sum > 255) || (sum > 0 && cIP[0] =='0')||num>3) return false;//若子段的值>255或为0开头的非0子段或“.”的数目>3,则为无效IP

		if (*p != '\0') p++;
		n = 0;
	}
	if (num != 3) return false;
	return true;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值