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

6 篇文章 1 订阅

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值