Epoll高并发通讯类

// 通用库
namespace Common
{
	// EpollSocket.h
	namespace Thread
	{
		class CEpollSocket
		{
		public:
			CEpollSocket();
			virtual ~CEpollSocket();

		public:
			static const int FD_EXIT = -1; // 通知退出FD
			static const int WAIT_NUM = 32; // 等待数量
			static const int LISTEN_NUM = 32; // 监听数量
			static const int BUFF_LEN = 4096; // 接收缓冲区大小
			static const int EPL_CRT_NUM = 256; // EPOLL创建数量

		public:
			// 接收数据结构
			struct SocketData
			{
				int fdSocket;
				string strAddr;
				unsigned int nSize;
				char buff[BUFF_LEN];
			};

			typedef const SocketData* const PSocketData;
			typedef void(*CallBackFunc)(void* const, PSocketData);

		public:
			// 创建服务
			bool Open(const std::string& strIP, unsigned int nPort = 0);
			// 运行服务
			bool Run(bool *bExit, unsigned int nThreadNum = 1);
			// 设置回调参数句柄
			void SetHandle(void* const pHandle);
			// 设置数据接收回调函数
			void SetDataRecv(CallBackFunc const pFunc);
			// 设置客户端连接回调函数
			void SetCltConnect(CallBackFunc const pFunc);
			// 设置客户端断开连接回调函数
			void SetCltDisConnect(CallBackFunc const pFunc);

		private:
			// 私有化拷贝赋值构造函数,禁止实例复制
			CEpollSocket(const CEpollSocket&);
			CEpollSocket& operator =(const CEpollSocket&);
			// 设置为非阻塞模式
			bool SetNonBlocking(int fdSocket);
			// 读书数据线程函数
			static void* ReadData(void* pParam);

		private:
			int m_fdEpoll; // EPOLL句柄
			int m_fdListen; // 监听句柄
			void* m_pHandle; // 回调参数句柄
			Thread::CThreadBuffer<int> m_fdBuff; // 客户端读取句柄缓冲区
			CallBackFunc m_pOnDataRecv; // 接收数据回调函数
			CallBackFunc m_pOnCltConnect; // 客户端连接回调函数
			CallBackFunc m_pOnCltDisConnect; // 客户端断开连接回调函数
		};

		typedef CEpollSocket::PSocketData PSocketData;
	}


	// EpollSocket.cpp
	namespace Net
	{
		extern const int CEpollSocket::FD_EXIT;

		CEpollSocket::CEpollSocket()
		{
			m_fdEpoll = 0;
			m_fdListen = 0;
			m_pHandle = NULL;
			m_pOnDataRecv = NULL;
			m_pOnCltConnect = NULL;
			m_pOnCltDisConnect = NULL;
		}

		CEpollSocket::~CEpollSocket()
		{
			//dtor
		}


		//
		//【函数说明】: 设置为非阻塞模式
		//【输入参数】: fdSocket 通讯句柄
		//【输出参数】:
		//【返回值】: true 成功;false 失败
		//

		bool CEpollSocket::SetNonBlocking(int fdSocket)
		{
			int iOpts = 0;

			iOpts = fcntl(fdSocket, F_GETFL);
			if (iOpts < 0)
			{
				std::cerr << "fcntl F_GETFL fail" << std::endl;
				return false;
			}

			iOpts = iOpts | O_NONBLOCK;
			if (fcntl(fdSocket, F_SETFL, iOpts) < 0)
			{
				std::cerr << "fcntl F_SETFL fail" << std::endl;
				return false;
			}

			return true;
		}


		//
		//【函数说明】: 创建服务
		//【输入参数】: strIP IP地址;nPort 端口号
		//【输出参数】:
		//【返回值】: true 成功;false 失败
		//

		bool CEpollSocket::Open(const std::string& strIP, unsigned int nPort)
		{
			struct epoll_event epEvent;
			struct sockaddr_in addrSrv;

			m_fdEpoll = epoll_create(EPL_CRT_NUM);
			m_fdListen = socket(AF_INET, SOCK_STREAM, 0);
			if (m_fdEpoll < 1 || m_fdListen < 1)
			{
				std::cerr << "epoll_create or socket fail" << std::endl;
				return false;
			}

			// 把socket设置为非阻塞方式
			if (!SetNonBlocking(m_fdListen))
			{
				return false;
			}

			// 设置与要处理的事件相关的文件描述符
			epEvent.data.fd = m_fdListen;

			// 设置要处理的事件类型
			epEvent.events = EPOLLIN | EPOLLET;

			// 注册epoll事件
			if (epoll_ctl(m_fdEpoll, EPOLL_CTL_ADD, m_fdListen, &epEvent) < 0)
			{
				std::cerr << "epoll_ctl fail" << std::endl;
				return false;
			}

			// 设置服务地址信息
			bzero(&addrSrv, sizeof(addrSrv));
			addrSrv.sin_family = AF_INET;
			addrSrv.sin_port = htons(nPort);
			if (strIP.empty())
			{
				addrSrv.sin_addr.s_addr = INADDR_ANY;
			}
			else
			{
				addrSrv.sin_addr.s_addr = inet_addr(strIP.c_str());
			}

			// 绑定地址
			if (bind(m_fdListen, (sockaddr*)&addrSrv, sizeof(addrSrv)) < 0)
			{
				std::cerr << "bind fail" << std::endl;
				return false;
			}

			// 开始监听
			if (listen(m_fdListen, LISTEN_NUM) < 0)
			{
				std::cerr << "listen fail" << std::endl;
				return false;
			}

			return true;
		}


		//
		//【函数说明】: 运行服务
		//【输入参数】: bExit 退出标志;nThreadNum 读数据线程数量
		//【输出参数】:
		//【返回值】: true 成功;false 失败
		//

		bool CEpollSocket::Run(bool *bExit, unsigned int nThreadNum)
		{
			pthread_t* pThReadData = new pthread_t[nThreadNum];

			// 启动读数据线程
			for (unsigned int i = 0; i < nThreadNum; i++)
			{
				if (pthread_create(&pThReadData[i], NULL, ReadData, this) != 0)
				{
					std::cerr << "pthread_create fail" << std::endl;
					goto Exit;
				}
			}

			while (!*bExit)
			{
				unsigned int nFds = 0;
				struct epoll_event epEvents[WAIT_NUM];

				// 等待事件发生
				nFds = epoll_wait(m_fdEpoll, epEvents, WAIT_NUM, -1);

				// 处理所发生的所有事件
				for (unsigned int i = 0; i < nFds; ++i)
				{
					// 有新客户端连接
					if (epEvents[i].data.fd == m_fdListen)
					{
						int fdClient = 0;
						struct sockaddr_in addrClt;
						struct epoll_event epEvent;
						socklen_t nAddrSize = sizeof(addrClt);

						fdClient = accept(m_fdListen, (sockaddr*)&addrClt, &nAddrSize);
						if (fdClient < 0)
						{
							std::cerr << "accept fail" << std::endl;
							return false;
						}

						// 把socket设置为非阻塞方式
						if (!SetNonBlocking(fdClient))
						{
							return false;
						}

						// 设置用于读操作的文件描述符、注测的读操作事件
						epEvent.data.fd = fdClient;
						epEvent.events = EPOLLIN | EPOLLET;

						// 注册事件
						if (epoll_ctl(m_fdEpoll, EPOLL_CTL_ADD, fdClient, &epEvent) < 0)
						{
							return false;
						}

						// 通知客户端连接
						if (NULL != m_pOnCltConnect)
						{
							SocketData socketData;
							socketData.fdSocket = fdClient;
							socketData.strAddr = inet_ntoa(addrClt.sin_addr);
							m_pOnCltConnect(m_pHandle, &socketData);
						}
					}
					else if (epEvents[i].events & EPOLLIN)
					{
						m_fdBuff.Push(epEvents[i].data.fd);
					}
				}
			}

		Exit:

			// 通知读取线程退出
			for (unsigned int i = 0; i < nThreadNum; i++)
			{
				m_fdBuff.Push(FD_EXIT);
				pthread_join(pThReadData[i], NULL);
			}
			delete[] pThReadData;

			return true;
		}


		//
		//【函数说明】: 设置回调参数句柄
		//【输入参数】: pHandle 回调参数句柄
		//【输出参数】:
		//【返回值】:
		//

		void CEpollSocket::SetHandle(void* const pHandle)
		{
			m_pHandle = pHandle;
		}


		//
		//【函数说明】: 设置数据接收回调函数
		//【输入参数】: pHandle 数据接收回调函数
		//【输出参数】:
		//【返回值】:
		//

		void CEpollSocket::SetDataRecv(CallBackFunc const pFunc)
		{
			m_pOnDataRecv = pFunc;
		}


		//
		//【函数说明】: 设置客户端连接回调函数
		//【输入参数】: pHandle 客户端连接回调函数
		//【输出参数】:
		//【返回值】:
		//

		void CEpollSocket::SetCltConnect(CallBackFunc const pFunc)
		{
			m_pOnCltConnect = pFunc;
		}


		//
		//【函数说明】: 设置客户端端口连接回调函数
		//【输入参数】: pHandle 客户端端口连接回调函数
		//【输出参数】:
		//【返回值】:
		//

		void CEpollSocket::SetCltDisConnect(CallBackFunc const pFunc)
		{
			m_pOnCltDisConnect = pFunc;
		}


		//
		//【函数说明】: 读书数据线程函数
		//【输入参数】: pParam 类实例地址
		//【输出参数】:
		//【返回值】:
		//

		void* CEpollSocket::ReadData(void* pParam)
		{
			int nSize = 0;
			SocketData socketData;
			CEpollSocket* pEpollSocket = (CEpollSocket *)pParam;

			while (true)
			{
				errno = 0;
				memset(&socketData, 0, sizeof(socketData));
				pEpollSocket->m_fdBuff.Pop(socketData.fdSocket);
				if (socketData.fdSocket == FD_EXIT)
				{
					std::cout << "read data thread exit" << std::endl;
					break;
				}

				// 循环接收数据
				do
				{
					nSize = recv(socketData.fdSocket, socketData.buff + socketData.nSize, BUFF_LEN - socketData.nSize, 0);
					if (nSize > 0)
					{
						socketData.nSize += nSize;
					}
					else
					{
						break;
					}
				} while (true);

				// 判断是否为连接断开
				if (socketData.nSize == 0 && errno != EINTR && errno != EAGAIN)
				{
					// 连接断开
					struct epoll_event epEvent;

					epEvent.data.fd = socketData.fdSocket;
					epEvent.events = EPOLLIN | EPOLLET;
					close(socketData.fdSocket);
					epoll_ctl(pEpollSocket->m_fdEpoll, EPOLL_CTL_DEL, socketData.fdSocket, &epEvent);

					// 通知客户端断开连接
					if (NULL != pEpollSocket->m_pOnCltDisConnect)
					{
						pEpollSocket->m_pOnCltDisConnect(pEpollSocket->m_pHandle, &socketData);
					}

					continue;
				}

				// 回掉函数,通知处理数据
				if (pEpollSocket->m_pOnDataRecv != NULL)
				{
					pEpollSocket->m_pOnDataRecv(pEpollSocket->m_pHandle, &socketData);
				}
			}

			return NULL;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值