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