libevent2 简单服务器客户端实现
基本功能,应该比较容易扩展
windows版本,我用的vc编译,linux去掉wsa相关内容,将sprintf_s替换成snprintf应该就可以了。
server.cpp
/*
socket server
*/
#include <iostream>
#include <signal.h>
#define WIN32
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libevent_core.lib")
#pragma comment(lib,"libevent_extras.lib")
using std::cout;
using std::cerr;
using std::endl;
void p_evconnlistener_cb(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
void p_bufferevent_read_cb(bufferevent* pBufferEvent, void* pUserData);
void p_bufferevent_write_cb(bufferevent* pBufferEvent, void* pUserData);
void p_bufferevent_event_cb(bufferevent* pBufferEvent, short what, void* pUserData);
void p_sigint_cb(evutil_socket_t fd, short what, void* pUserData);
int main()
{
cout << "server start" << endl;
// windows网络初始化
WSAData wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
// 初始化eventbase
event_base* pEventBase = event_base_new();
if (pEventBase == NULL)
{
cerr << "event_base fail" << endl;
return 1;
}
// 定制监听地址端口
sockaddr_in server_addr;
size_t server_addr_len = sizeof(sockaddr_in);
memset(&server_addr, 0, server_addr_len);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(ADDR_ANY);
server_addr.sin_port = htons(7777);
// 代替bind&listen方法的方法……
evconnlistener* pEvConnListener = evconnlistener_new_bind(pEventBase,
p_evconnlistener_cb,
pEventBase,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
-1,
(sockaddr*)&server_addr,
server_addr_len);
// 监听<C-c>中断信号
event* pSigintEvent = evsignal_new(pEventBase, SIGINT, p_sigint_cb, pEventBase);
if ((!pSigintEvent) || (event_add(pSigintEvent, NULL) < 0))
{
cerr << "sigint event fail." << endl;
return 1;
}
// 循环处理消息
event_base_dispatch(pEventBase);
// 清理
evconnlistener_free(pEvConnListener);
event_free(pSigintEvent);
event_base_free(pEventBase);
// windows网络停止
WSACleanup();
cout << "server stop" << endl;
return 0;
}
// 连接回调
void p_evconnlistener_cb(evconnlistener* pEvConnListener, evutil_socket_t fd, sockaddr* addr, int socklen, void* userData)
{
event_base* pEventBase = (event_base*)userData;
// 绑定连接的socket和bufferevent(这里的socket默认就是非阻塞的)
bufferevent* pBufferEvent = bufferevent_socket_new(pEventBase, fd, BEV_OPT_CLOSE_ON_FREE);
if (!pBufferEvent)
{
cerr << "bufferevent fail." << endl;
event_base_loopbreak(pEventBase);
return;
}
// 添加监听socket,设置回调
bufferevent_setcb(pBufferEvent, p_bufferevent_read_cb, p_bufferevent_write_cb, p_bufferevent_event_cb, NULL);
// 设置处理(监听?)内容:读、写
bufferevent_enable(pBufferEvent, EV_READ | EV_WRITE);
}
// 数据读取回调
void p_bufferevent_read_cb(bufferevent* pBufferEvent, void* pUserData)
{
// 读数据
char info[255];
size_t infoLen = bufferevent_read(pBufferEvent, info, sizeof(info));
if (infoLen < sizeof(info))
info[infoLen] = 0;
cout << "recv info:" << info << endl;
// 返回数据
char info_ret[255];
sprintf_s(info_ret, sizeof(info_ret), "server ret:%s", info);
bufferevent_write(pBufferEvent, info_ret, strlen(info_ret));
}
// 写数据回调
void p_bufferevent_write_cb(bufferevent* pBufferEvent, void* pUserData)
{
cout << "react client complete." << endl;
}
// 事件处理(出错、socket中断)
void p_bufferevent_event_cb(bufferevent* pBufferEvent, short what, void* pUserData)
{
if (what & BEV_EVENT_READING)
cout << "event_cb BEV_EVENT_READING." << endl;
if (what & BEV_EVENT_WRITING)
cout << "event_cb BEV_EVENT_WRITING." << endl;
if (what & BEV_EVENT_EOF)
cout << "event_cb BEV_EVENT_EOF." << endl;
if (what & BEV_EVENT_ERROR)
cout << "event_cb BEV_EVENT_ERROR." << endl;
if (what & BEV_EVENT_TIMEOUT)
cout << "event_cb BEV_EVENT_TIMEOUT." << endl;
if (what & BEV_EVENT_CONNECTED)
cout << "event_cb BEV_EVENT_CONNECTED." << endl;
bufferevent_free(pBufferEvent);
}
// <C-c>中断信号处理
void p_sigint_cb(evutil_socket_t fd, short what, void* pUserData)
{
event_base* pEventBase = (event_base*)pUserData;
timeval delay = { 2, 0 };
cout << "stop service in 2 seconds." << endl;
event_base_loopexit(pEventBase, &delay);
}
client.cpp
/*
libevent临时测试客户端
*/
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using std::cout;
using std::endl;
int main()
{
WSAData wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(7777);
size_t addrLen = sizeof(addr);
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
cout << "create sock error." << WSAGetLastError() << endl;
return 0;
}
if (connect(sock, (const sockaddr*)&addr, addrLen) == SOCKET_ERROR)
{
cout << "connect error." << WSAGetLastError() << endl;
closesocket(sock);
return 0;
}
char info[255];
for (int i = 0; i < 10; ++i)
{
sprintf_s(info, sizeof(info), "client msg:%d", i);
send(sock, info, strlen(info), 0);
memset(info, 0, sizeof(info));
int infoLen = recv(sock, info, sizeof(info), 0);
if (infoLen == 0)
{
cout << "sock closed." << endl;
closesocket(sock);
sock = NULL;
break;
}
else if (infoLen < 0)
{
cout << "recv error." << WSAGetLastError() << endl;
closesocket(sock);
sock = NULL;
break;
}
else
{
cout << "recv info:" << info << endl;
}
}
if (sock)
{
closesocket(sock);
sock = NULL;
}
WSACleanup();
return 0;
}