您的位置:首页 > 理论基础 > 计算机网络

使用libevent和boost编写一个简单的tcp服务器

2017-03-28 00:00 239 查看
摘要: 使用libevent和boost编写一个简单的tcp服务器

写这个东西主要是为了学习libevent的基本用法,以及学习下boost的线程库。
程序结构比较简单:

  1. 首先是建立一个监听socke。

  2. 将这个监听的socket绑定到一个event事件上,然后等待有客户过来连接。

  3. 如果响应到监听socket可读,则accept尝试连接的客户端。

  4. 开启一个线程来处理所有和这个连接过来的客户端之间的交互。(实际上什么事情也没做,就是cout了下每次recv的数据大小)

代码如下:
1. 首先是程序入口,main函数
main函数主要是注册了一个监听使用的socket。另外一旦进入了监听状态,就不好退出程序,所以一开始就注册了一个信号响应函数,
专门用来响应程序退出的信号。

//创建监听socket,然后等待这个socket有客户来链接
//每个链接一个线程去处理
int main(int argc, char* argv[])
{
//首先处理好kill -2信号
struct sigaction sigact = {0};
sigact.sa_sigaction = On_Exit;
if ( -1 == sigaction(2, &sigact, NULL))
{
log("建立响应函数失败");
return 0;
}

//建立一个非阻塞的socket句柄
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (-1 == sockfd)
{
log("创建监听socket失败", ERROR);
return 0;
}
log("创建监听socket成功");

//bind
sockaddr_in sinAddr;
sinAddr.sin_family = AF_INET;
sinAddr.sin_port = htons(LISTEN_PORT);
sinAddr.sin_addr.s_addr = inet_addr("172.21.169.160");
if( -1 == bind(sockfd, (sockaddr*)&sinAddr, sizeof(sockaddr_in)))
{
log("监听本地端口失败", ERROR);
return 0;
}

//进入listen
if (-1 == listen(sockfd, SOMAXCONN))
{
log("监听本地端口失败", ERROR);
return 0;
}
log("监听本地端口成功");

//进入accept状态
log("即将进入监听状态");
EnterAcceptWhile(sockfd);

close(sockfd);
log("监听线程成功结束了");
return 0;
}

2.  进入libevent的消息循环
//基于event的消息响应机制
void EnterAcceptWhile(int nFd)
{
if (g_pEventLoop == NULL)
g_pEventLoop = event_base_new();

//绑定事件
struct event* pListenEvent = event_new(g_pEventLoop, nFd, EV_PERSIST|EV_READ, On_Sock_Accept, NULL);
if (pListenEvent == NULL)
{
log ("建立监听事件失败");
return;
}

//加入监听事件,持续
event_add(pListenEvent, NULL);

//分派消息
int nLoopRst = 0;
if (0 == (nLoopRst = event_base_dispatch(g_pEventLoop)))
{
log("事件循环正常停止了");
}
else if (1 == nLoopRst)
{
log("没有事件关联到这个消息循环了");
}
else
{
log("消息循环出现了错误", ERROR);
}

event_free(pListenEvent);
event_base_free(g_pEventLoop);
g_pEventLoop = NULL;
}

3. 一旦响应到有客户端过来连接,就会进入On_Sock_Accept函数。因此这个函数应该尽可能的短小。
//响应客户端连接
void On_Sock_Accept(int nFd, short sFlags, void* pArg)
{
if (!(sFlags&EV_READ))
{
log("接受到了一个莫名其妙的消息", WARNING);
return;
}
log("响应到一个客户端过来连接了");

socklen_t sockLen;
sockaddr sa;
int nAcceptFd = accept(nFd, &sa, &sockLen);

//这里应该启动一个线程来处理这个请求事务的,而不应该在这里做大量的复杂操作
SocketThread st(nAcceptFd);
boost::thread thread_ST(st);
}

4. 在上个函数中,开了一个线程专门处理来自这个客户端的请求。 类SocketThread的代码如下:
SocketThread::SocketThread(int nFd)
{
m_nFd = nFd;
}

void SocketThread::operator() ()
{
if (m_nFd == -1)
return;

//读取数据,等到读取完成之后,输出出来,最后关闭掉socket连接
char* pszTemp = new char[1024];
memset(pszTemp, 0, 1024);

int nRecvSize = 0;
while( 0 < (nRecvSize = recv(m_nFd, pszTemp, 1024, 0)))
{
//读到数据了
std::cout << "thread id: " << boost::this_thread::get_id() << " 接受到了:" << nRecvSize << "字节的数据" << std::endl;
}

delete[] pszTemp;
pszTemp = NULL;

if (nRecvSize == 0)
{
std::cout << "客户端已经关闭了" << std::endl;
}
else if (nRecvSize < 0)
{
std::cout << "接受客户端数据失败,请检查原因" << std::endl;
}
close(m_nFd);
m_nFd = -1;
}

5. 最后是响应信号2, 退出libevent的消息循环
其实这里存在问题:如果退出消息循环的时候还有很多的工作者线程正在运行,应该要先让他们把事情做完再退出的。

//响应退出消息
void On_Exit(int nSigId, siginfo_t* pSigInfo, void* pArg)
{
log("准备结束监听了", WARNING);
if (g_pEventLoop != NULL)
{
//打破监听循环
event_base_loopbreak(g_pEventLoop);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: