libevent学习一:windows上简单的libevent例子

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qilimi1053620912/article/details/80704823
    这里开发主要在windows上开发,熟悉的同学可以修改少量代码移植到linux上去,由于在windows上方便的原因,学习主要还是在windows上面开发,而且linevent是支持跨平台的,所以也可以不用太在意。

上一篇我们已经编译好了libevent,版本是2.1.8,使用的vs 版本是vs2017。

这里给大家推荐一下:http://www.wangafu.net/~nickm/libevent-book/   ;有能力的可以去libevent的官网(主要是英语能力),官方写得还是很好很全面的。

这里我们先了解一下libevent,libevent是一个事件驱动库,不能仅说它是一个网络库,在这里我也作为一个初学者,和大家讲一下我的了解,后面会持续更新。

现在我们就把libevent当成支持网络通讯的事件驱动库,来实现一个简单的libevent服务客户端程序:

客户端:

流程:

1、加载套接字库 (windows的socket通讯基础可以知道);

2、连接服务端;

3、初始化一个event_base对象,创建一个event对象,将socket和处理接收输出的回调函数赋通过event_new赋值给event对象,然后将event对象加入事件循环;

4、创建一个线程,接收界面输入发送给服务端;

5、开启事件循环;

6、关闭wsa。

代码:

#include “stdafx.h”
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include “event2/bufferevent.h”
#include “event2/buffer.h”
#include “event2/listener.h”
#include “event2/util.h”
#include “event2/event.h”
#include <event2/event-config.h>
#include <WinSock2.h>
#include
#define IP_ADDRESS (“127.0.0.1”)
#define PORT (9951)
int m_isrun = 0;
int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, char* msg);
void socket_read_cb(int fd, short events, void *arg);
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
char t_cin[1024];
int sockfd = (int)lpParameter;
while (1) {
memset(t_cin, 0, 1024);

	std::cin >> t_cin;

	if (strcmp(t_cin, "exit") == 0) {
		break;
	}
	cmd_msg_cb(sockfd, t_cin);
}
exit(1);
return 0;

}
int main(int argc, char** argv)
{
//加载套接字库
WSADATA wsaData;
int iRet = 0;
iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iRet != 0)
{
return -1;
}

if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
{
	WSACleanup();
	return -1;
}
//两个参数依次是服务器端的IP地址、端口号  
int sockfd = tcp_connect_server(IP_ADDRESS, PORT);
if (sockfd == -1)
{
	perror("tcp_connect error ");
	return -1;
}
printf("connect to server successful\n");
struct event_base* base = event_base_new();
struct event *ev_sockfd = event_new(base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, NULL);
event_add(ev_sockfd, NULL);
//创建线程发送数据
HANDLE hThread1 = CreateThread(NULL, 0, Fun1Proc, (void*)sockfd, 0, NULL);
CloseHandle(hThread1);
event_base_dispatch(base);
WSACleanup();
printf("finished \n");
return 0;

}
void cmd_msg_cb(int fd, char* msg)
{
//把终端的消息发送给服务器端
int ret = send(fd, (const char*)msg, strlen((char*)msg), 0);
if (ret <= 0)
{
perror("read fail ");
return;
}
printf(“send:%s\n”, (char*)msg);
}
void socket_read_cb(int fd, short events, void arg)
{
char msg[1024];
//为了简单起见,不考虑读一半数据的情况
int len = recv(fd, msg, sizeof(msg) - 1, 0);
if (len <= 0)
{
perror("read fail ");
exit(1);
}
msg[len] = ‘\0’;
printf(“recv %s from server\n”, msg);
}
int tcp_connect_server(const char
server_ip, int port)
{
int sockfd, status, save_errno;
SOCKADDR_IN server_addr;

memset(&server_addr, 0, sizeof(server_addr));

server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(server_ip);
server_addr.sin_port = htons(port);

sockfd = ::socket(PF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
	return sockfd;


status = ::connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

if (status == -1)
{
	save_errno = errno;   //清理  
	closesocket(sockfd);
	errno = save_errno; //the close may be error  
	return -1;
}

evutil_make_socket_nonblocking(sockfd);

return sockfd;

}

服务端:

流程:

1、加载套接字库

2、初始化tcp的socket,监听绑定端口;

3、创建event_base和event对象,将等待连接的回调函数和socket赋值给event对象,然后加入事件循环,开始事件循环;

4、关闭套接字库。

代码:

#include<stdio.h>
#include<string.h>
#include<errno.h>
#include <signal.h>
#include “event2/bufferevent.h”
#include “event2/buffer.h”
#include “event2/listener.h”
#include “event2/util.h”
#include “event2/event.h”
#include <event2/event-config.h>
#include <WinSock2.h>

#define IP_ADDRESS (“127.0.0.1”)
#define PORT (9951)

void accept_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg);
int tcp_server_init(int port, int listen_num);

int main(int argc, char** argv)
{
//加载套接字库
WSADATA wsaData;
int iRet = 0;
iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iRet != 0)
{
//cout << “WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!” << endl;;
return -1;
}
if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
{
WSACleanup();
//cout << “WSADATA version is not correct!” << endl;
return -1;
}
int listener = tcp_server_init(PORT, 10);
if (listener == -1)
{
perror(" tcp_server_init error ");
return -1;
}
struct event_base* base = event_base_new();
//添加监听客户端请求连接事件
struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,
accept_cb, base);
event_add(ev_listen, NULL);
event_base_dispatch(base);
WSACleanup();
return 0;
}

void accept_cb(int fd, short events, void* arg)
{
evutil_socket_t sockfd;
struct sockaddr_in client;
socklen_t len = sizeof(client);
sockfd = ::accept(fd, (struct sockaddr*)&client, &len);
evutil_make_socket_nonblocking(sockfd);
printf(“accept a client %d\n”, sockfd);
struct event_base* base = (event_base*)arg;
//仅仅是为了动态创建一个event结构体
struct event ev = event_new(NULL, -1, 0, NULL, NULL);
//将动态创建的结构体作为event的回调参数
event_assign(ev, base, sockfd, EV_READ | EV_PERSIST,
socket_read_cb, (void
)ev);
event_add(ev, NULL);
}

void socket_read_cb(int fd, short events, void *arg)
{
char msg[4096];
struct event ev = (struct event)arg;
int len = recv(fd, msg, sizeof(msg) - 1, 0);
if (len <= 0)
{
printf(“some error happen when read\n”);
event_free(ev);
closesocket(fd);
return;
}
msg[len] = ‘\0’;
printf(“recv the client msg: %s\n”, msg);
char reply_msg[4096] = "I have recvieced the msg: ";
strcat(reply_msg + strlen(reply_msg), msg);
int ret = send(fd, reply_msg, strlen(reply_msg), 0);
}

int tcp_server_init(int port, int listen_num)
{
int errno_save;
int listener;
listener = ::socket(AF_INET, SOCK_STREAM, 0);
if (listener == -1)
return -1;
//允许多次绑定同一个地址。要用在socket和bind之间
evutil_make_listen_socket_reuseable(listener);
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) {
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
if (::listen(listener, listen_num) < 0) {
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
//跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener);
return listener;
}

附上github工程地址:https://github.com/QilimiWxu/testlibevent
————————————————
版权声明:本文为CSDN博主「qilimi-1」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qilimi1053620912/article/details/80704823

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值