代码
libevent网上使用栗子很多,最基本的使用无外乎几个接口的使用,附上libevent文档说明:https://www.monkey.org/~provos/libevent/doxygen-2.0.1/index.html
在libevent基础上,我们编写:服务端&客户端来进行理解,最基本的用法。
- 服务端的创建
//#define _USE_WINSOCK
// EV_READ : 只要网络缓冲中还有数据,回调函数就会被触发
// EV_WRITE : 只要塞给网络缓冲的数据被写完,回调函数就会被触发
// EV_PERSIST : 不指定这个属性的话,回调函数被触发后事件会被删除
void CServerSocket::CreateServer( const DWORD v_dwPort )
{
SOCKADDR_IN saServerAddress;
ZERO_MEMORY(&saServerAddress, sizeof(SOCKADDR_IN));
saServerAddress.sin_family = AF_INET;
saServerAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
saServerAddress.sin_port = htons(v_dwPort);
#ifdef _USE_WINSOCK //采用传统的bind和listen
evutil_socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
if (bind(sock, (SOCKADDR*)&saServerAddress, sizeof(saServerAddress)) == SOCKET_ERROR)
{
printf("Create Server Failed.[bind]Err=<%d>\n", WSAGetLastError());
return;
}
if (listen(sock, 10) == SOCKET_ERROR)
{
printf("Create Server Failed.[listen]Err=<%d>\n", WSAGetLastError());
return;
}
m_pBase = event_base_new();
struct event* listenEvent = event_new(m_pBase, sock, EV_READ | EV_PERSIST, do_accept_cb, (void*)m_pBase);
event_add(listenEvent, NULL);
printf("Create Server Success. Listen At <%d>\n", v_dwPort);
printf("Wait For Client Connect...\n");
event_base_dispatch(m_pBase); //阻塞
event_free(listenEvent);
event_base_free(m_pBase);
#else //采用libevent的listen事件,绑定端口及监听
m_pBase = event_base_new();
struct evconnlistener* listener = evconnlistener_new_bind(m_pBase, listener_cb, (void*)m_pBase, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
-1, (SOCKADDR*)&saServerAddress, sizeof(saServerAddress));
printf("Create Server Success. Listen At <%d>\n", v_dwPort);
printf("Wait For Client Connect...\n");
event_base_dispatch(m_pBase);
evconnlistener_free(listener);
event_base_free(m_pBase);
#endif
printf("Close Server...\n");
}
void listener_cb(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* addr, int socklen, void* ctx);
void do_accept_cb(evutil_socket_t sock, short event, void* ctx);
void read_cb_s(struct bufferevent *bev, void* ctx);
void write_cb_s(struct bufferevent *bev, void *ctx);
void event_cb_s(struct bufferevent *bev, short what, void* ctx);
//
void listener_cb(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* addr, int socklen, void* ctx)
{
printf("Accept Cilent=<%u><%s><%d>\n", fd, inet_ntoa(((sockaddr_in*)addr)->sin_addr), ntohs(((sockaddr_in*)addr)->sin_port));
struct event_base* base = (struct event_base*)ctx;
struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, read_cb_s, write_cb_s, event_cb_s, base);
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
void do_accept_cb(evutil_socket_t sock, short event, void* ctx)
{
struct event_base* base = (struct event_base*)ctx;
evutil_socket_t fd;
SOCKADDR_IN saClientAddress;
int iLen = sizeof(saClientAddress);
fd = accept(sock, (SOCKADDR*)&saClientAddress, &iLen);
if (SOCKET_ERROR == fd)
{
printf("Accept Client Connect Failed.[accept]Err=<%d>\n", WSAGetLastError());
return;
}
printf("Accept Cilent=<%u><%s><%d>\n", fd, inet_ntoa(saClientAddress.sin_addr), ntohs(saClientAddress.sin_port));
struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, read_cb_s, write_cb_s, event_cb_s, ctx);
bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
}
void read_cb_s(struct bufferevent *bev, void *ctx)
{
evutil_socket_t fd = bufferevent_getfd(bev);
char szLine[MAX_PATH + 1] = {0};
int n = 0;
while (n = bufferevent_read(bev, szLine, MAX_PATH))
{
szLine[n] = '\0';
printf("fd=%u, read line: %s\n", fd, szLine);
//bufferevent_write(bev, szLine, n);
}
}
void write_cb_s(struct bufferevent *bev, void *ctx)
{
printf("Send Welcome...\n");
//bufferevent_write(bev, "Welcome...", 10);
}
void event_cb_s(struct bufferevent *bev, short what, void *ctx)
{
evutil_socket_t fd = bufferevent_getfd(bev);
printf("fd=%u, ", fd);
if (what & BEV_EVENT_TIMEOUT)
{
printf("Timed out\n"); //if bufferevent_set_timeouts() called
}
else if (what & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if (what & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
bufferevent_free(bev);
}
- 客户端的创建
void CClientSocket::CreateClient(const char* v_szServerIP, const DWORD v_dwServerPort)
{
SOCKADDR_IN saAddress;
ZERO_MEMORY(&saAddress, sizeof(saAddress));
saAddress.sin_family = AF_INET;
saAddress.sin_addr.S_un.S_addr = inet_addr(v_szServerIP);
saAddress.sin_port = htons(v_dwServerPort);
SOCKET fd = socket(AF_INET, SOCK_STREAM, 0);
if (SOCKET_ERROR == fd)
{
printf("Create Client Failed.[socket]Err=<%d>\n", WSAGetLastError());
return;
}
m_pBase = event_base_new();
struct bufferevent* bev = bufferevent_socket_new(m_pBase, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, read_cb_c, write_cb_c, event_cb_c, NULL);
bufferevent_socket_connect(bev, (SOCKADDR*)&saAddress, sizeof(saAddress));
bufferevent_enable(bev, EV_READ | EV_WRITE);
event_base_dispatch(m_pBase);
event_base_free(m_pBase);
}
void read_cb_c(struct bufferevent *bev, void *ctx)
{
//取得输入、输出缓冲区
struct evbuffer* input = bufferevent_get_input(bev);
struct evbuffer* output = bufferevent_get_output(bev);
//从input缓冲区前面复制并移除sizeof(buffer)字节数据到buffer处的内存处
//如果字节数不够sizeof(buffer),则复制并移除所有字节
char buffer[1024] = {0};
int n = 0;
while ( (n = evbuffer_remove(input, buffer, sizeof(buffer))) > 0 )
{
printf("Client recv:%s\n", buffer);
}
char szSend[] = "Client send ok...";
evbuffer_add(output, szSend, strlen(szSend));
}
void write_cb_c(struct bufferevent *bev, void *ctx)
{
printf("Send Ok....\n");
}
void event_cb_c(struct bufferevent *bev, short what, void *ctx)
{
if (what & BEV_EVENT_EOF) //遇到文件结束指示
{
bufferevent_free(bev);
printf("Connection closed.\n");
}
else if (what & BEV_EVENT_ERROR) //操作发生错误
{
bufferevent_free(bev);
printf("Got an error on the connection: %d\n", WSAGetLastError());
}
else if (what & BEV_EVENT_TIMEOUT) //超时
{
bufferevent_free(bev);
printf("Connection timeout.\n");
}
else if (what & BEV_EVENT_CONNECTED) //连接已经完成
{
printf("connect succeed.\n");
//客户端链接成功后,给服务器发送第一条消息
char *reply_msg = "I receive a message from client";
bufferevent_write(bev, reply_msg, strlen(reply_msg));
}
}
libevent处理网络事件,都是采用回调的方式处理,所以需要注册读写回调等。详细等后续深入研究源码,这边贴上,此项目代码链接:https://download.csdn.net/download/fzuim/11041669