跨平台C++通过libevent实现高并发网络编程

Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,已经被广泛的应用,作为底层的网络库,可适用于多种平台。该项目使用了libevent的库。

为了实现高性能的服务端,用了异步处理网络IO+多线程。

启动一个专门用来accept客户端的线程,启动指定个数的工作线程用来处理与客户端的具体业务,accept线程将收到的客户端随机分发到工作线程中,每个工作线程可能处理一个或多个与客户端的交互。

TcpServer.h

//TCP服务基础类
	class TcpServer
	{
	private:
		int m_ThreadCount;					//子线程数
		int m_Port;							//监听的端口
		LibeventThread *m_MainBase;			//accept线程的libevent事件处理机
		LibeventThread *m_Threads;			//存储各个工作子线程信息的数组
		map<int, event*> m_SignalEvents;	//自定义的信号处理		
		
	public:
		static const int EXIT_CODE = -1;

	private:
		//初始化子线程的数据
		void SetupThread(LibeventThread *thread);	

		//子线程的入门函数
		void WorkerLibevent(void *arg);

		//(主线程收到请求后),对应子线程的处理函数
		static void ThreadProcess(int fd, short which, void *arg);

		//被libevent回调的各个静态函数
		static void ListenerEventCb(evconnlistener *listener, evutil_socket_t fd, sockaddr *sa, int socklen, void *user_data);
		static void ReadEventCb(struct bufferevent *bev, void *data);
		static void WriteEventCb(struct bufferevent *bev, void *data); 
		static void CloseEventCb(struct bufferevent *bev, short events, void *data);

		//监听线程
		static void TcpServerStart(void *arg);

	protected:
		//这五个虚函数,一般是要被子类继承,并在其中处理具体业务的
		//新建连接成功后,会调用该函数
		virtual void ConnectionEvent(Conn *conn) { }

		//读取完数据后,会调用该函数
		virtual void ReadEvent(Conn *conn) { }

		//发送完成功后,会调用该函数(因为串包的问题,所以并不是每次发送完数据都会被调用)
		virtual void WriteEvent(Conn *conn) { }

		//断开连接(客户自动断开或异常断开)后,会调用该函数
		virtual void CloseEvent(Conn *conn, short events) { }

		//发生致命错误(如果创建子线程失败等)后,会调用该函数
		//该函数的默认操作是输出错误提示,终止程序
		virtual void ErrorQuit(const char *str);

		//检查该IP连接是否已经存在
		virtual void CloseExistConnection(uint32_t nClientIp) { }



	public:
		TcpServer(int count);
		~TcpServer();

		//设置监听的端口号,如果不需要监听,请将其设置为EXIT_CODE
		void SetPort(int port)
		{ m_Port = port; }

		//获取端口号
		int GetPort()
		{ return m_Port; }

		//开始事件循环
		bool StartRun();

		//在tv时间里结束事件循环
		//否tv为空,则立即停止
		void StopRun(timeval *tv);

		//添加和删除信号处理事件
		//sig是信号,ptr为要回调的函数
		bool AddSignalEvent(int sig, void (*ptr)(int, short, void*));
		bool DeleteSignalEvent(int sig);

		//添加和删除定时事件
		//ptr为要回调的函数,tv是间隔时间,once决定是否只执行一次
		event *AddTimerEvent(void(*ptr)(int, short, void*), timeval tv, bool once);
		bool DeleteTImerEvent(event *ev);

	};

TcpServer.cpp

TcpServer::TcpServer(int count)
	{
	#ifdef WIN32
		evthread_use_windows_threads();//win上设置
	#else
		evthread_use_pthreads();    //unix上设置
	#endif

		//初始化各项数据
		m_ThreadCount = count;
		m_Port = -1;
		m_MainBase = new LibeventThread;
		m_Threads = new LibeventThread[m_ThreadCount];

		m_MainBase->base = event_base_new();

		//初始化各个子线程的结构体
		for(int i = 0; i < m_ThreadCount; i++)
		{
			SetupThread(&m_Threads[i]);
		}
	}

	TcpServer::~TcpServer()
	{
		//停止事件循环(如果事件循环没开始,则没效果)
		StopRun(NULL);

		//释放内存
		event_base_free(m_MainBase->base);
		for(int i=0; i<m_ThreadCount; i++)
		{
			event_base_free(m_Threads[i].base);
		}		

		delete m_MainBase;
		delete [] m_Threads;
	}

	void TcpServer::ErrorQuit(const char *str)
	{
		//输出错误信息  
	}


	void TcpServer::SetupThread(LibeventThread *me)
	{
	#ifdef WIN32
		evthread_use_windows_threads();//win上设置
	#else
		evthread_use_pthreads();    //unix上设置
	#endif
		//建立libevent事件处理机制
		me->tcpConnect = this;
		me->base = event_base_new();
		if( NULL == me->base )
		{
			LOG("TcpServer::SetupThread event_base_new failed!")
			return;
		}

		evthread_make_base_notifiable(me->base);
		//在主线程和子线程之间建立管道
		evutil_socket_t fds[2] = {-1, -1};

	#ifdef WIN32
		if (evutil_socketpair(AF_INET, SOCK_STREAM, 0, fds) < 0)
		{
			LOG("TcpServer::SetupThread evutil_socketpair failed!")
			return;
		}
	#else
		if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
		{
			LOG("TcpServer::SetupThread evutil_socketpair failed!")
			return;
		}
	#endif

		LOG("TcpServer::SetupThread notifySendFd = " << fds[0] << ", notifyReceiveFd = " << fds[1])

		me->notifySendFd = fds[0];
		me->notifyReceiveFd = fds[1]; 

		//让子线程的状态机监听管道
		event_set(&me->notifyEvent, me->notifyReceiveFd, EV_READ | EV_PERSIST, ThreadProcess, me);
		event_base_set(me->base, &me->notifyEvent);
		if (event_add(&me->notifyEvent, 0) == -1)
		{
			LOG("TcpServer::SetupThread event_add failed!")
		}
	}

	void TcpServer::WorkerLibevent(void *arg)
	{
		//开启libevent的事件循环,准备处理业务
		LibeventThread *me = (LibeventThread*)arg;
		event_base_dispatch(me->base);
	}

	bool TcpServer::StartRun()
	{
		boost::shared_ptr<boost::thread> sessionThread;
		sessionThread.reset( new boost::thread(boost::bind(&TcpServer::TcpServerStart, this) ) );
		return true;
	}

	void TcpServer::TcpServerStart(void *arg)
	{
		TcpServer *pthis_tcpServerManager = (TcpServer *)arg;
		evconnlistener *listener;

		//如果端口号不是EXIT_CODE,就监听该端口号
		if(pthis_tcpServerManager->m_Port != EXIT_CODE)
		{
			struct sockaddr_in sin;

			memset(&sin, 0, sizeof(sin));
			sin.sin_family = AF_INET;
			sin.sin_port = htons(pthis_tcpServerManager->m_Port);

			listener = evconnlistener_new_bind(pthis_tcpServerManager->m_MainBase->base, ListenerEventCb, (void*)arg,
												LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_THREADSAFE, -1,
												(struct sockaddr*)&sin, sizeof(sin));
			if( NULL == listener )
			{
				LOG("TcpServer::StartRun evconnlistener_new_bind failed!") 
				return;
			}
		}

		//开启各个子线程
		for(int i=0; i<pthis_tcpServerManager->m_ThreadCount; i++)
		{
		   pthis_tcpServerManager->m_Threads[i].tid.reset(new boost::thread(boost::bind(&TcpServer::WorkerLibevent, pthis_tcpServerManager, (void*)&(pthis_tcpServerManager->m_Threads[i]))));
		}

		//开启主线程的事件循环
		event_base_dispatch(pthis_tcpServerManager->m_MainBase->base);

		//事件循环结果,释放监听者的内存
		if(pthis_tcpServerManager->m_Port != EXIT_CODE)
		{
			evconnlistener_free(listener);
		}

		LOG("TcpServer::StartRun evconnlistener_new_bind successed!") 
		return;
	}


	void TcpServer::StopRun(timeval *tv)
	{
		int contant = EXIT_CODE;
		//向各个子线程的管理中写入EXIT_CODE,通知它们退出
		for(int i=0; i<m_ThreadCount; i++)
		{
			write(m_Threads[i].notifySendFd, (char *)&contant, sizeof(int));
		}
		//结果主线程的事件循环
		event_base_loopexit(m_MainBase->base, tv);
	}

	void TcpServer::ListenerEventCb(struct evconnlistener *listener, 
					  evutil_socket_t fd,
					  struct sockaddr *sa, 
					  int socklen, 
					  void *user_data)
	{		
		TcpServer *server = (TcpServer*)user_data;
		struct sockaddr_in *sin = (struct sockaddr_in *)sa;

		//判断如果不在允许表则关闭连接
		uint32_t nClientIp = htonl(sin->sin_addr.s_addr);

		//判断如果该IP连接已经存在怎么关闭之前的连接,重新连接
		server->CloseExistConnection(nClientIp);

		evutil_make_socket_nonblocking(fd);
		evutil_make_socket_closeonexec(fd);

		//随机选择一个子线程,通过管道向其传递socket描述符
		int num = rand() % server->m_ThreadCount;
		int sendfd = server->m_Threads[num].notifySendFd;
		write(sendfd, (char *)&fd, sizeof(evutil_socket_t));
		LOG("TcpEventServerE::ListenerEventCb send socket " << fd << " by send pipe " << sendfd)
	}

	void TcpServer::ThreadProcess(int fd, short which, void *arg)
	{
		LOG("TcpServer::ThreadProcess receive pipe =" << fd)
		LibeventThread *me = (LibeventThread*)arg;

		//从管道中读取数据(socket的描述符或操作码)
		int pipefd = me->notifyReceiveFd;
		evutil_socket_t confd;
		read(pipefd, (char *)&confd, sizeof(evutil_socket_t));

		LOG("TcpServer::ThreadProcess thread receive socket " << confd)

		//如果操作码是EXIT_CODE,则终于事件循环
		if( EXIT_CODE == confd )
		{
			LOG("TcpServer::ThreadProcess EXIT_CODE")
			event_base_loopbreak(me->base);
			return;
		}

		//新建连接
		struct bufferevent *bev;
		bev = bufferevent_socket_new(me->base, confd, BEV_OPT_THREADSAFE | BEV_OPT_CLOSE_ON_FREE);
		if (!bev)
		{
			LOG("TcpServer::ThreadProcess bufferevent_socket_new failed!")
			event_base_loopbreak(me->base);
			return;
		}

		//将该链接放入队列
		Conn *conn = me->connectQueue.InsertConn(confd, me);
		//准备从socket中读写数据
		bufferevent_setcb(bev, ReadEventCb, WriteEventCb, CloseEventCb, conn);

		struct timeval tv;
		tv.tv_sec = LINKDOWN_TIME;
		tv.tv_usec = 0; 
		bufferevent_set_timeouts(bev, &tv, &tv);

		bufferevent_enable(bev, EV_WRITE | EV_READ);

		//调用用户自定义的连接事件处理函数
		me->tcpConnect->ConnectionEvent(conn);
	}

	void TcpServer::ReadEventCb(struct bufferevent *bev, void *data)
	{
		Conn *conn = (Conn*)data;
		conn->m_ReadBuf = bufferevent_get_input(bev);
		conn->m_WriteBuf = bufferevent_get_output(bev);

		//调用用户自定义的读取事件处理函数
		conn->m_Thread->tcpConnect->ReadEvent(conn);
	} 

	void TcpServer::WriteEventCb(struct bufferevent *bev, void *data)
	{
		Conn *conn = (Conn*)data;
		conn->m_ReadBuf = bufferevent_get_input(bev);
		conn->m_WriteBuf = bufferevent_get_output(bev);

		//调用用户自定义的写入事件处理函数
		conn->m_Thread->tcpConnect->WriteEvent(conn);
	}

	void TcpServer::CloseEventCb(struct bufferevent *bev, short events, void *data)
	{				
		Conn *conn = (Conn*)data;
		LOG("TcpServer::CloseEventCb socketId=" << conn->GetFd())
		//调用用户自定义的断开事件处理函数
		conn->m_Thread->tcpConnect->CloseEvent(conn, events);
		conn->GetThread()->connectQueue.DeleteConn(conn);

		if (bev)
		{
			bufferevent_free(bev);
		}
	}

	bool TcpServer::AddSignalEvent(int sig, void (*ptr)(int, short, void*))
	{
		//新建一个信号事件
		event *ev = evsignal_new(m_MainBase->base, sig, ptr, (void*)this);
		if (!ev || event_add(ev, NULL) < 0)
		{
			event_del(ev);
			return false;
		}

		//删除旧的信号事件(同一个信号只能有一个信号事件)
		DeleteSignalEvent(sig);
		m_SignalEvents[sig] = ev;

		return true;
	}

	bool TcpServer::DeleteSignalEvent(int sig)
	{
		map<int, event*>::iterator iter = m_SignalEvents.find(sig);
		if(iter == m_SignalEvents.end())
		{
			return false;
		}		

		event_del(iter->second);
		m_SignalEvents.erase(iter);
		return true;
	}

	event *TcpServer::AddTimerEvent(void (*ptr)(int, short, void *), 
					  timeval tv, bool once)
	{
		int flag = 0;
		if(!once)
		{
			flag = EV_PERSIST;
		}		

		//新建定时器信号事件
		struct event *ev = NULL;
		event_assign(ev, m_MainBase->base, -1, flag, ptr, (void*)this);
		if(event_add(ev, &tv) < 0)
		{
			event_del(ev);
			return NULL;
		}
		return ev;
	}

	bool TcpServer::DeleteTImerEvent(event *ev)
	{
		int res = event_del(ev);
		return (0 == res);
	}

其中Conn代表每个socket连接

Conn.h

class Conn
	{
		//此类只能由TcpServer创建, 并由ConnQueue类管理
		friend class ConnQueue;
		friend class TcpServer;

	private:
		const int m_fd;				//socket的ID
		evbuffer *m_ReadBuf;		//读数据的缓冲区
		evbuffer *m_WriteBuf;		//写数据的缓冲区
		Conn *m_Prev;				//前一个结点的指针
		Conn *m_Next;				//后一个结点的指针
		LibeventThread *m_Thread;

		Conn(int fd=0);
		~Conn();

	public:

		LibeventThread *GetThread() { return m_Thread; }
		int GetFd() { return m_fd; }

		//获取可读数据的长度
		int GetReadBufferLen()
		{ return evbuffer_get_length(m_ReadBuf); }


		//从读缓冲区中取出len个字节的数据,存入buffer中,若不够,则读出所有数据
		//返回读出数据的字节数
		int GetReadBuffer(char *buffer, int len)
		{ 
			return evbuffer_remove(m_ReadBuf, buffer, len);
		}

		//从读缓冲区中复制出len个字节的数据,存入buffer中,若不够,则复制出所有数据
		//返回复制出数据的字节数
		//执行该操作后,数据还会留在缓冲区中,buffer中的数据只是原数据的副本
		int CopyReadBuffer(char *buffer, int len)
		{ 
			return evbuffer_copyout(m_ReadBuf, buffer, len);
		}

		//获取可写数据的长度
		int GetWriteBufferLen()
		{ return evbuffer_get_length(m_WriteBuf); }


		//将数据加入写缓冲区,准备发送,不会马上发送,会粘包,设备并没有处理粘包,不能用改函数
		int AddToWriteBuffer(char *buffer, int len)
		{ 
			return write(m_fd, buffer, len);
		}

		//将读缓冲区中的数据移动到写缓冲区
		void MoveBufferData()
		{ evbuffer_add_buffer(m_WriteBuf, m_ReadBuf); }
	};

Conn.cpp

Conn::Conn(int fd) : m_fd(fd)
	{
	  m_Prev = NULL;
	  m_Next = NULL;
	}

	Conn::~Conn()
	{
	}

ConnQueue是个所有socket连接的双链表类

ConnQueue.h

//带头尾结点的双链表类,每个结点存储一个连接的数据
	class ConnQueue
	{
	private:
		Conn *m_head;
		Conn *m_tail;

	public:
		ConnQueue();
		~ConnQueue();

		Conn *InsertConn(int fd, LibeventThread *t);
		void DeleteConn(Conn *c);
	};

ConnQueue.cpp

ConnQueue::ConnQueue()
	{
		//建立头尾结点,并调整其指针
		m_head = new Conn(0);
		m_tail = new Conn(0);
		m_head->m_Prev = m_tail->m_Next = NULL;
		m_head->m_Next = m_tail;
		m_tail->m_Prev = m_head;
	}

	ConnQueue::~ConnQueue()
	{
		Conn *tcur, *tnext;
		tcur = m_head;
		//循环删除链表中的各个结点
		while( tcur != NULL )
		{
			tnext = tcur->m_Next;
			delete tcur;
			tcur = tnext;
		}
	}

	Conn *ConnQueue::InsertConn(int fd, LibeventThread *t)
	{
		Conn *c = new Conn(fd);
		c->m_Thread = t;
		Conn *next = m_head->m_Next;

		c->m_Prev = m_head;
		c->m_Next = m_head->m_Next;
		m_head->m_Next = c;
		next->m_Prev = c;
		return c;
	}

	void ConnQueue::DeleteConn(Conn *c)
	{
		c->m_Prev->m_Next = c->m_Next;
		c->m_Next->m_Prev = c->m_Prev;
		delete c;
	}

LibeventThread代表每个工作线程的线程信息

LibeventThread.h

//每个子线程的线程信息
	struct LibeventThread
	{
		boost::shared_ptr<boost::thread> tid;				//线程的ID
		struct event_base *base;	//libevent的事件处理机
		struct event notifyEvent;	//监听管理的事件机

		int notifyReceiveFd;		//管理的接收端
		int notifySendFd;			//管道的发送端

		ConnQueue connectQueue;		//socket连接的链表

		//在libevent的事件处理中要用到很多回调函数,不能使用类隐含的this指针
		//所以用这样方式将TcpServer的类指针传过去
		TcpServer *tcpConnect;	 //TcpServer类的指针

	};

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: libevent是一个开源的C语言网络编程库,主要用于处理高并发网络连接。它提供了对事件驱动的支持,使得开发者可以方便地编写高效的并发网络应用程序。 libevent的核心是事件循环机制。在传统的网络编程中,通常需要使用多线程或多进程来处理并发连接,而使用libevent可以通过一个事件循环来处理多个连接。在事件循环中,可以注册多个事件,并定义回调函数来处理事件的触发。当有事件发生时,libevent会调用相应的回调函数来处理事件的处理逻辑。这样可以大大简化并发编程的复杂性,并提高程序的性能。 libevent的事件模型基于操作系统提供的I/O多路复用机制,如select、poll和epoll等。它可以在不同的操作系统平上运行,并提供一致的接口和高效的事件处理机制。借助这些机制,libevent可以同时处理大量的并发连接,并保持低延迟和高吞吐量。 除了处理网络连接,libevent还提供了其他常用的功能,如定时器和信号处理等。它允许开发者在事件循环中注册定时器事件,可以用于定时任务的调度。同时,libevent还可以处理来自操作系统的信号,并提供了对信号的处理接口,以便开发者能够处理各种系统事件。 总之,libevent是一个功能强大、简单易用的高并发网络编程库,适用于开发各种类型的网络应用。无论是开发服务器、代理、聊天程序还是实时应用,libevent都能帮助开发者快速编写高性能的并发网络程序。 ### 回答2: libevent是一个开源的C/C++网络库,用于高性能的事件驱动编程。它提供了一个轻量级、可移植的框架,用于开发高并发网络应用程序。 它的设计目标是提供一个高效的事件处理器,可以处理成千上万个并发连接,并且支持多线程并发处理。libevent基于事件驱动模型,通过异步I/O和回调函数来实现高并发处理网络请求。 libevent提供了一系列的函数来注册和监听各种网络事件,包括读、写、超时和信号等等。当一个事件发生时,libevent会调用相应的回调函数来处理事件。通过这种方式,我们可以非常方便地处理并发连接,并实现高性能的网络编程libevent的优点主要包括: 1. 高性能:libevent使用异步I/O和事件驱动模型,能够处理成千上万个并发连接,具有很高的处理能力。 2. 可移植性:libevent提供了统一的接口,可以在多种操作系统上运行,包括Linux、Windows、Mac等。 3. 易用性:libevent简单易用,只需注册感兴趣的事件和相应的回调函数,就可以实现高效的网络编程。 4. 多线程支持:libevent支持多线程并发处理,可以充分利用多核CPU的性能优势。 总之,libevent是一款非常适合高并发网络编程的开源库,它可以帮助我们实现高性能的服务器程序,提升系统的并发处理能力。无论是开发网络服务器还是网络应用程序,libevent都是一个不错的选择。 ### 回答3: libevent 是一个用于高并发网络编程的 C/C++ 库。它提供了一个跨平台的异步事件驱动的网络编程框架,能够实现高效地处理大量并发连接的需求。 libevent 的主要特点包括: 1. 异步事件驱动:libevent 使用事件驱动模型,主要利用非阻塞 I/O 和事件回调机制,能够高效地处理大量并发事件。 2. 跨平台支持:libevent 提供了跨不同操作系统的支持,包括 Windows、Linux、Unix 等,并且提供了统一的 API 接口,方便开发者进行跨平台开发。 3. 支持多种网络协议:libevent 支持 TCP、UDP、HTTP 等多种网络协议,为开发者提供了丰富的网络编程能力。 4. 高性能:libevent 的设计目标之一是高性能,它通过使用多路复用技术,将系统资源高效地利用起来,能够同时处理大量并发连接,并且保持低延迟。 5. 灵活易用:libevent 提供了简洁的 API,使用起来非常方便,可以快速实现高并发网络编程的需求。 总之,libevent 是一个强大而灵活的 C/C++ 库,适用于各种需要处理高并发连接的网络应用程序。无论是开发高性能服务器、代理、负载均衡器还是其他类似应用,libevent 都是一个值得推荐的选择。它的高效性能、跨平台支持和简洁易用的 API 接口使得开发者能够快速构建稳定可靠的高并发网络应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值