在libevent事件基础上实现一个TCPServer类

说明:实现的目标为,本地启动一个TCP服务器,接收来自客户端的连接和数据,采用多线程和回调的方式,方便上层调用。由于初学,如果有哪边问题,请务必指出。

/***********************************************************************MyTcpServer.h**********************************************************/

#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <WS2tcpip.h >
#include "event2/event.h"
#include "event2/bufferevent.h"
#include "event2/thread.h"
#include "event2/listener.h"
#include <WinSock2.h>


#define MAX_LINE  1024
//实现以下的所有函数
void do_accept(evutil_socket_t listener, short event, void *arg);//接受连接
void read_cb(struct bufferevent *bev, void *arg); //读
void error_cb(struct bufferevent *bev, short event, void *arg);//错误
void write_cb(struct bufferevent *bev, void *arg); //写
typedef void (CALLBACK* DEALPROC)(SOCKET s,struct bufferevent *bev,unsigned char *buf,int len,char *IP);//处理接收数据
typedef void (CALLBACK* DEALERROR)(SOCKET s,char *IP);//处理错误


typedef struct NODE
{
struct event_base*base;//基础对象
struct event *listen_event;//监听事件
}Node;


class MyTcpServer
{
public:
MyTcpServer(void);
~MyTcpServer(void);
int GetErrcode();
BOOL CreateServer(int port,int backlog);
BOOL StopServer();//停止服务
DEALPROC lpDealFunc; //处理数据
DEALERROR   lpDealError;//处理

protected:
BOOL m_bWSAStartup;
int err;
evutil_socket_t listener;

public:
int SetDealFunc(DEALPROC lpDealFunc);// 设置回调函数
int SetDealError(DEALERROR lpDealError);// 设置错误
BOOL IsSended(SOCKET s);
BOOL IsSockConnected(SOCKET s);
};



/*******************************************************************************MyTcpServer.cpp***************************************************/

#include "MyTcpServer.h"
#include <vector>
using namespace std;


MyTcpServer *gThis = NULL; //保存对象
vector<Node> m_baseArray;


//每有一个连接开启一个处理的线程
void MyThread(LPARAM pa)
{
evutil_socket_t fd = (evutil_socket_t)pa;
evthread_use_windows_threads();
struct event_base *base = event_base_new();
if (base == NULL)
{
return ;
}


struct event *listen_event;
listen_event = event_new(base, fd, EV_READ|EV_PERSIST, do_accept, (void*)base);
event_add(listen_event, NULL);
Node newBase;
newBase.base = base;
newBase.listen_event = listen_event;
m_baseArray.push_back(newBase);
OutputDebugString("添加一个新的连接事件循环");
event_base_dispatch(base);//进入事件循环
//跳出事件循环,关闭监听
OutputDebugString("return\n");
}






MyTcpServer::MyTcpServer(void)
{
WSADATA wsadata;
m_bWSAStartup = FALSE;
err = 0;
listener = 0;


if(WSAStartup(0x202, &wsadata) == 0)
{
m_bWSAStartup = TRUE;
err = 0;
}
else
{
m_bWSAStartup = FALSE;
err = WSAGetLastError();
}

}



MyTcpServer::~MyTcpServer(void)
{
if(m_bWSAStartup) WSACleanup();
}




int MyTcpServer::GetErrcode()
{
return err;
}


//创建一个TCP Server
BOOL MyTcpServer::CreateServer(int port,int backlog)
{
gThis = this;
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0)
{
err = WSAGetLastError();
return FALSE;
}
if(evutil_make_listen_socket_reuseable(listener) < 0) return FALSE;
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
err = WSAGetLastError();
return FALSE;
}
if (listen(listener, backlog) < 0) 
{
err = WSAGetLastError();
return FALSE;
}


if(evutil_make_socket_nonblocking(listener) < 0)  return FALSE;




//创建一个线程来进入监听事件的循环
HANDLE m_hServerHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)MyThread,(LPVOID)listener,CREATE_SUSPENDED,0);
if (m_hServerHandle)
{
ResumeThread(m_hServerHandle);
}else
{
return FALSE;
}


return TRUE;
}


void do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = (struct event_base *)arg;
evutil_socket_t fd;
struct sockaddr_in sin;
socklen_t slen;
slen = sizeof(struct sockaddr_in);
char *pIPdata = new char[20];
memset(pIPdata,0,20);
fd = accept(listener, (struct sockaddr *)&sin, &slen);
if (fd <0)
{
OutputDebugString("fd < 0");
return ;
}else
{
OutputDebugString("fd ");
}
memcpy(pIPdata,inet_ntoa(sin.sin_addr),20);
struct bufferevent *bev = bufferevent_socket_new(base, fd,BEV_OPT_CLOSE_ON_FREE);

bufferevent_setcb(bev, read_cb,NULL, error_cb, pIPdata);
bufferevent_enable(bev, EV_READ|EV_PERSIST|EV_WRITE);
}


void read_cb(struct bufferevent *bev, void *arg)
{
char line[MAX_LINE+1];
memset(line,0,MAX_LINE+1);
int n;
evutil_socket_t fd = bufferevent_getfd(bev);


while (n = bufferevent_read(bev, (char *)line, MAX_LINE), n > 0) {
//处理数据
gThis->lpDealFunc(fd,bev,(unsigned char*)line,n,(char *)arg);
//OutputDebugString("222");
}
}


void error_cb(struct bufferevent *bev, short event, void *arg)
{
evutil_socket_t fd = bufferevent_getfd(bev);
printf("fd = %u, ", fd);
if (event & BEV_EVENT_TIMEOUT) {
printf("Timed out\n"); //if bufferevent_set_timeouts() called
}
else if (event & BEV_EVENT_EOF) {
OutputDebugString("connection closed\n");
gThis->lpDealError(fd,(char *)arg);
delete[] arg;
arg = NULL;
}
else if (event & BEV_EVENT_ERROR) {
printf("some other error\n");
}
bufferevent_free(bev);
}


// 设置回调函数
int MyTcpServer::SetDealFunc(DEALPROC lpDealFunc)
{
this->lpDealFunc = lpDealFunc;
return 0;
}



int MyTcpServer::SetDealError(DEALERROR lpDealError)
{
this->lpDealError = lpDealError;
return 0;
}


BOOL MyTcpServer::IsSended(SOCKET s)
{
int nRet = 0;
struct fd_set Fd_Send;
struct timeval Time_Send;
memset(&Fd_Send, 0, sizeof(struct fd_set));
FD_CLR(s, &Fd_Send); 
FD_SET(s, &Fd_Send); 
Time_Send.tv_sec = 2;
Time_Send.tv_usec = 0;
nRet = select(s,NULL, &Fd_Send, NULL, &Time_Send);
if (nRet > 0)
{
return TRUE;
}
return FALSE;
}


BOOL MyTcpServer::IsSockConnected(SOCKET s)
{
int nRet = 0;
struct fd_set Fd_Recv;
struct timeval Time_Recv;
memset(&Fd_Recv, 0, sizeof(struct fd_set));
FD_CLR(s, &Fd_Recv); 
FD_SET(s, &Fd_Recv); 
Time_Recv.tv_sec = 2;
Time_Recv.tv_usec = 0;
nRet = select(s, &Fd_Recv, NULL, NULL, &Time_Recv);
return (nRet == 0);
}


//停止服务
BOOL MyTcpServer::StopServer()
{
for (vector<Node>::iterator iter = m_baseArray.begin(); iter != m_baseArray.end(); iter ++)
{
event_base_loop(iter->base,EVLOOP_ONCE);
//event_base_free(iter->base);
event_free(iter->listen_event);
}
m_baseArray.clear();
closesocket(listener);
WSACleanup();
return TRUE;

}

#endif


补充:如果向socket发送数据,采用普通的send等函数就可以。

发布了61 篇原创文章 · 获赞 6 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/shayueqing/article/details/51273918