使用libevent实现回显服务器

说明

使用libevent实现了一个回显一行字符串的服务器:客户端发送一行字符串,以'\n'结尾,服务器接受完一行后就原封不动地发回给客户端。以下实现未使用bufferevent,使用libevent的bufferevent,代码会更简单。

libevent教程可参看http://www.wangafu.net/~nickm/libevent-book/01_intro.html

代码

cpp 复制代码
#include <iostream>
#include "event2/event.h"
#include "event2/thread.h"
#include <set>
#include <process.h>

#pragma comment(lib, "event.lib")
#pragma comment(lib, "Ws2_32.lib")

struct ClientSocketItem
{
	ClientSocketItem()
	{
		hSocket = NULL;
		memset(szRecv, 0, sizeof(szRecv));
		nRecvSize = 0;
		bNeedWrite = false;
		nWriteOffset = 0;
		pReadEvent = nullptr;
		pWriteEvent = nullptr;
	}

	SOCKET hSocket;
	struct event* pReadEvent;
	struct event* pWriteEvent;

	char szRecv[1024];
	unsigned int nRecvSize;
	std::string strIp;

	bool bNeedWrite;//接受完成,可以发送
	unsigned int nWriteOffset = 0;
};

std::set<ClientSocketItem*> g_Clients;



void onTimeOut(evutil_socket_t fd, short events, void * pParams )
{
	std::cout << "time is out" << std::endl;
}

bool InitSocket()
{
	WORD wVersionRequested = MAKEWORD(2, 2);
	WSADATA wsaData = { 0 };
	int err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return false;
	}

	if (LOBYTE(wsaData.wVersion) != 2 ||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return false;
	}

	return true;
}

void CloseClientItem(ClientSocketItem* pItem)
{
	if (!pItem)
	{
		return;
	}

	evutil_closesocket(pItem->hSocket);
	event_del(pItem->pReadEvent);
	event_del(pItem->pWriteEvent);

	event_free(pItem->pReadEvent);
	event_free(pItem->pWriteEvent);

	delete pItem;
	g_Clients.erase(pItem);
}


void onClientRead(evutil_socket_t fd, short events, void* pParam)
{
	ClientSocketItem* pClient = (ClientSocketItem*)pParam;
	if (!pClient)
	{
		return;
	}

	//std::cout << "read callback" << std::endl;

	char c = 0; //测试用,每次只读一个字符
	int nRecvValue = recv(pClient->hSocket, &c, 1, 0);
	if (nRecvValue > 0)
	{
		pClient->szRecv[pClient->nRecvSize] = c;
		pClient->nRecvSize += 1;
		std::cout << "read one char: " << c << std::endl;
		if (c == '\n')
		{
			std::cout << "read finished" << std::endl;
			pClient->bNeedWrite = true;
		}

		return;
	}
	else if (0 == nRecvValue)
	{
		std::cout << "peer client closed" << std::endl;
		CloseClientItem(pClient);
		return ;
	}
	else
	{
		int nError = WSAGetLastError();
		if (WSAEWOULDBLOCK != nError)
		{
			std::cerr << "recv failed with error " << nError << std::endl;
			CloseClientItem(pClient);
			return ;
		}

		std::cout << "next recv" << std::endl;
		return;
	}
}

void onClientWrite(evutil_socket_t fd, short events, void* pParam)
{
	ClientSocketItem* pClient = (ClientSocketItem*)pParam;
	if (!pClient)
	{
		return;
	}

	//std::cout << "write callback" << std::endl;

	if (!pClient->bNeedWrite)
	{
		return;
	}

	//测试用,每次只发送一个字符
	int nRet = send(pClient->hSocket, pClient->szRecv + pClient->nWriteOffset, 1, 0);
	if (nRet >= 0)
	{
		std::cout << "send one char: " << pClient->szRecv[pClient->nWriteOffset] << std::endl;

		pClient->nWriteOffset += 1;
		if (pClient->nWriteOffset == pClient->nRecvSize)
		{
			std::cout << "send finished, close client(" << pClient->strIp.c_str()
				<< ")" << std::endl;
			pClient->bNeedWrite = false;
			CloseClientItem(pClient);
			return;
		}

		return;
	}
	else
	{
		int nError = WSAGetLastError();
		if (WSAEWOULDBLOCK != nError)
		{
			std::cerr << "send failed with error " << nError << std::endl;
			CloseClientItem(pClient);
			return;
		}

		std::cout << "next send" << std::endl;
		return;
	}
}


void onEventListen(evutil_socket_t fd, short events, void * pParam)
{
	event_base* pEventBase = (event_base*)(pParam);

	sockaddr_in mPeerAddr = { 0 };
	int nAddrLen = sizeof(sockaddr);
	evutil_socket_t hClientSocket = accept(fd, (sockaddr*)(&mPeerAddr), &nAddrLen);
	if (INVALID_SOCKET == hClientSocket)
	{
		std::cout << "accept failed with error "
			<< WSAGetLastError() << std::endl;
		return;
	}

	ClientSocketItem* pClient = new ClientSocketItem();
	pClient->hSocket = hClientSocket;
	g_Clients.insert(pClient);
	std::cout << "accept success" << std::endl;

	pClient->pReadEvent = event_new(pEventBase, hClientSocket,
		EV_READ | EV_PERSIST, onClientRead, pClient);
	pClient->pWriteEvent = event_new(pEventBase, hClientSocket,
		EV_WRITE | EV_PERSIST, onClientWrite, pClient);

	event_add(pClient->pReadEvent, nullptr);
	event_add(pClient->pWriteEvent, nullptr);
	
}

unsigned __stdcall WorkThreadFun(void* pParam)
{
	event_base* pEventBase = (event_base*)pParam;

	int nRet = event_base_dispatch(pEventBase);

	std::cout << "event loop quit with " << nRet << std::endl;
	return 0;
}


int main(int argc, char** argv)
{
	if (!InitSocket())
	{
		return -1;
	}

	evutil_socket_t hListenSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == hListenSocket)
	{
		std::cerr << "create socket failed with error " << WSAGetLastError()
			<< std::endl;
		return -1;
	}
	evutil_make_socket_nonblocking(hListenSocket);

	sockaddr_in mSockAddrIn = { 0 };
	mSockAddrIn.sin_family = AF_INET;
	mSockAddrIn.sin_port = htons((u_short)8878);
	//mSockAddrIn.sin_addr.S_un.S_addr = inet_addr("0.0.0.0");
	InetPton(AF_INET, TEXT("0.0.0.0"), &(mSockAddrIn.sin_addr));
	if (SOCKET_ERROR == bind(hListenSocket, (sockaddr*)(&mSockAddrIn),
		sizeof(sockaddr)))
	{
		std::cerr << "bind failed with error " << WSAGetLastError() << std::endl;
		return -1;
	}

	if (SOCKET_ERROR == listen(hListenSocket, SOMAXCONN))
	{
		std::cerr << "listen failed with error " << WSAGetLastError() << std::endl;
		return -1;
	}

	evthread_use_windows_threads();
	event_base* pEventBase = event_base_new();
	if (!pEventBase)
	{
		return -1;
	}

	//struct event* pTimeOutEvent = event_new(pEventBase, -1, EV_PERSIST, onTimeOut, nullptr);
	//timeval tv{ 3, 0 };
	//event_add(pTimeOutEvent, &tv);

	struct event* pListenEvent = event_new(pEventBase, hListenSocket,
		EV_READ | EV_PERSIST, onEventListen, pEventBase);
	event_add(pListenEvent, nullptr);

	_beginthreadex(nullptr, 0, WorkThreadFun, pEventBase, 0, nullptr);
//
//
//	int nRet = event_base_dispatch(pEventBase);
//
//	std::cout << "event loop quit with " << nRet << std::endl;
	while (true)
	{
		Sleep(3000);
	}


	return 0;
}
相关推荐
whowin6 天前
C程序员应该知道的最好的8个C编程框架
qt·libevent·gtk·libuv·glib·cmocka·ncurses
一眼万里*e3 个月前
libevent入门篇
c++·libevent
Philosophy4 个月前
libevent之evbuffer
libevent
dvlinker6 个月前
开源事件通知库libevent及网络连接管理模块bufferevent详解
libevent·回调·数据读写·开源事件通知库·网络连接管理·bufferevent·心跳处理
一只特立独行的猪( ﹡ˆoˆ﹡8 个月前
Libevent的使用及reactor模型
reactor·libevent
小胖西瓜9 个月前
如何在 libevent 中读取超过 4096 字节的数据
libevent
OopspoO1 年前
libevent学习——event_base
c语言·学习·libevent
OopspoO1 年前
libevent数据结构——TAILQ_结构体
c语言·学习·libevent
Stack Overflow?Tan901 年前
libevent源码学习6---链接监听器evconnlistener
libevent