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

    这里开发主要在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 <iostream>
#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

 

 

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Libevent是一个事件驱动的网络编程框架,而event.h是其核心头文件之一。该头文件定义了事件处理相关的结构体、函数和宏等内容。 下面是event.h中常用的一些定义和函数: ### 1.事件回调函数 ```c typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg); ``` 该类型定义了事件回调函数的原型,其中fd是事件所在的文件描述符,events是事件类型,arg是用户传入的参数。 ### 2.事件结构体 ```c struct event { event_callback_fn ev_callback; // 事件回调函数 int ev_fd; // 事件所在的文件描述符 short ev_events; // 事件类型 short ev_res; // 事件结果 struct event_base *ev_base; // 事件所属的event_base void *ev_arg; // 用户传入的参数 }; ``` 该结构体表示一个事件,其中ev_callback是事件回调函数,ev_fd是事件所在的文件描述符,ev_events是事件类型,ev_res是事件结果,ev_base是事件所属的event_base,ev_arg是用户传入的参数。 ### 3.事件类型 ```c #define EV_TIMEOUT 0x01 #define EV_READ 0x02 #define EV_WRITE 0x04 #define EV_SIGNAL 0x08 #define EV_PERSIST 0x10 #define EV_ET 0x20 ``` 该宏定义了事件类型,分别为超时事件、读事件、写事件、信号事件、持续事件和边缘触发事件。 ### 4.事件处理函数 ```c struct event_base *event_base_new(void); int event_base_dispatch(struct event_base *base); int event_base_loopexit(struct event_base *base, const struct timeval *tv); void event_base_free(struct event_base *base); ``` 这些函数用于创建event_base、处理事件、退出事件循环和释放event_base等操作。 以上是event.h中的一些常用内容,更多细节可以查看源码和官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值