1. 程式人生 > >在libevent事件基礎上實現一個TCPServer類

在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等函式就可以。