说明
使用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;
}