详解libevent网络库(二)---即时聊天通讯

3 篇文章 0 订阅
3 篇文章 7 订阅

使用bufferevent简单实现服务器与客户端之间的即时聊天通讯

目录

使用bufferevent简单实现服务器与客户端之间的即时聊天通讯

前言

为libevent造一个房子---架构分析图

创建套接字bufferevent

设置bufferevent回调函数

操控bufferevent中的数据

为libevent客户端实现打基础

为libevent服务端实现打基础


前言

上回我们说到初学libevent框架,使用利用event_new、fifo实现进程间通讯此为无缓冲方式,承上篇所述,本文将讲述使用bufferevent利用数据缓存区实现网络通讯,文章的最后将以此为例实现客户端与服务器之间的聊天通讯。

libevent入门篇(一)--初识libevent

为libevent造一个房子---架构分析图

  1. 盖房的套路(从外到内看)
  2. 装修属于自己的房间(由内而外)

数据缓冲区---bufferevent

是libevent为IO缓冲区操作提供的一种通用机制,bufferevent 由一个底层的传输端口(如套接字 )。

数据缓冲区是由读缓冲区写缓冲区两部分组成。

 

创建套接字bufferevent
 

使用函数bufferevent_socket_new(),创建bufferevent的套接字

// struct bufferevent也是一个 event
struct bufferevent * bufferevent_socket_new(
                     struct event_base *base,
                     evutil_socket_t fd,
                     enum bufferevent_options options
);
// 参数options: 例如:BEV_OPT_CLOSE_ON_FREE 释放 bufferevent 时关闭底层传输端口
// 成功时返回bufferevent,失败则返回NULL

当然我们也忘不了释放函数 bufferevent_free()

void bufferevent_free(struct bufferevent *bev);

接下来就是我们为所欲为的时刻了,把我们的房间装修成想要的样子(为缓冲区添加读写操作,设置读写回调函数)

设置bufferevent回调函数

 

使用函数bufferevent_setcb(),设置回调函数

//对bufferevent设置回调函数	
		void bufferevent_setcb(
				struct bufferevent *bufev,
				bufferevent_data_cb readcb,//使用 bufferevent_read()读取buff中数据信息
				bufferevent_data_cb writecb,//写回调只是提示你发生出去数据,没有实质作用				      
				bufferevent_event_cb eventcb, 				
				void *cbarg
		);
//读写回调函数typedef 
		typedef void (*bufferevent_data_cb)(
				struct bufferevent *bev, 
				void *ctx
		);

		typedef void (*bufferevent_event_cb)(
				struct bufferevent *bev,
				short events, 
				void *ctx
		);
//events参数:
//			EV_EVENT_READING: 读取操作时发生某事件,具体是哪种事件请看其他标志
//			BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志
//			BEV_EVENT_ERROR:  操作时发生错误
//			BEV_EVENT_TIMEOUT:发生超时
//			BEV_EVENT_EOF:    遇到文件结束指示。
//			BEV_EVENT_CONNECTED:请求的连接过程已经完成实现客户端的时候可以判断

//EVUTIL_SOCKET_ERROR():关于错误的更多信息
EV_EVENT_READING读取操作时发生某事件,具体是哪种事件请看其他标志
BEV_EVENT_WRITING写入操作时发生某事件,具体是哪种事件请看其他标志
BEV_EVENT_ERROR操作时发生错误
BEV_EVENT_TIMEOUT发生超时
BEV_EVENT_EOF遇到文件结束指示
BEV_EVENT_CONNECTED请求的连接过程已经完成实现客户端的时候可以判断
 

对回调函数的管理--设置回调函数为启动状态或禁用态:

 

可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。没有启用读取或者写入事件时,  bufferevent 将不会试图进行数据读取或者写入。

//开启
		void bufferevent_enable(
			struct bufferevent *bufev, 
			short events
		); 

//禁用, 对应的回调就不会被调用
		void bufferevent_disable(
			struct bufferevent *bufev, 
			short events
		); 


		short bufferevent_get_enabled(
			struct bufferevent *bufev
		); 

操控bufferevent中的数据

向bufferevent的输出缓冲区添加数据

	    int bufferevent_write(
				struct bufferevent *bufev,
				const void *data, 
				size_t size
		);

从bufferevent的输入缓冲区移除数据

	  size_t bufferevent_read(
				struct bufferevent *bufev, 
				void *data, 
				size_t size
       );

为libevent客户端实现打基础

在实现通讯之前,我们需要拥有connect()作为客户端的启动链接,这时我们需要调用bufferevent_socket_connect()函数---思维导向理解为socket编程中需要connect()

        int bufferevent_socket_connect( struct bufferevent *bev,
                            struct sockaddr *address,
                            int addrlen 
        );
  • address 和 addrlen 参数跟标准调用 connect() 的参数相同

 

  1. bufferevent 未设置套接字,该函数将为其分配一个新的流套接字, 并且设置状态为非阻塞

  2. bufferevent 已设置套接字,调用 bufferevent_socket_connect() 将告知libevent 套接字还未连接,直到连接成功之前不应该对其进行读取或者写入操作。

  3. 连接完成之前可以向输出缓冲区添加数据

为libevent服务端实现打基础

在实现通讯之前,我们需要拥有bind()绑定与listen()监听,当然libevent为我们提供了更好的选择就是evconnlistener_new_bind()

链接监听器 - evconnlistener

    struct evconnlistener *evconnlistener_new_bind(
                struct event_base *base, evconnlistener_cb cb, void *ptr,
                unsigned flags,
                int backlog,
                const struct sockaddr *sa, int socklen
    );

当然我们也可以使用evconnlistener_new()函数,这两个可以二者取其一使用,推荐使用bind处理起来更简单方便,原因在于其内部为我们实现了绑定操作,感兴趣的小伙伴可查看源码对其深入研究

     struct evconnlistener * evconnlistener_new( 
               struct event_base *base,                 
               evconnlistener_cb cb,
               void *ptr,
               unsigned flags, int backlog, evutil_socket_t fd
);

回调函数的编写

        typedef void (*evconnlistener_cb)( 
                struct evconnlistener *listener, evutil_socket_t sock,
                struct sockaddr *addr,
                int len,
                void *ptr
);    

注:两个 evconnlistener_new*()函数都分配和返回一个新的连接监听器对象。连接监听器使用 event_base 来得知什么时候在给定的监听套接字上有新的 TCP 连接到达时,监听器调用回调函数。

当然我们也忘不了释放函数 vconnlistener_free()

void evconnlistener_free(struct evconnlistener *lev);

对evconnlistener的管理--设置为启动状态或禁用态以及修改回调设置:

以下两个函数暂时禁止或者重新允许监听新连接

        int evconnlistener_disable(struct evconnlistener *lev);
        int evconnlistener_enable(struct evconnlistener *lev);

函数调整 evconnlistener 的回调函数和其参数

        void evconnlistener_set_cb( 
            struct evconnlistener *lev,
            evconnlistener_cb cb,
            void *arg 
);

demo

举个栗子?

//server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>

// 读缓冲区回调
void read_cb(struct bufferevent *bev, void *arg)
{
    char buf[1024] = {0};   
    bufferevent_read(bev, buf, sizeof(buf));
    char* p = "我已经收到了你发送的数据!";
    printf("client say: %s\n", buf);

}

// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{
    printf("我是写缓冲区的回调函数...您已发送\n"); 
}

// 事件
void event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF)
    {
        printf("connection closed\n");  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
        printf("some other error\n");
    }
    
    bufferevent_free(bev);    
    printf("buffevent 资源已经被释放...\n"); 
}


void send_cb(evutil_socket_t fd, short what, void *arg);
void cb_listener(
        struct evconnlistener *listener, 
        evutil_socket_t fd, 
        struct sockaddr *addr, 
        int len, void *ptr)
{
   printf("connect new client\n");

   struct event_base* base = (struct event_base*)ptr;
   // 通信操作
   // 添加新事件
   struct bufferevent *bev;
   bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

   // 给bufferevent缓冲区设置回调
   bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
   bufferevent_enable(bev, EV_READ);
   
   
      // 创建一个事件
    struct event* ev = event_new(base, STDIN_FILENO, 
                                 EV_READ | EV_PERSIST, 
                                 send_cb, bev);
	  event_add(ev, NULL);
   
}
void send_cb(evutil_socket_t fd, short what, void *arg)
{
    char buf[1024] = {0}; 
    struct bufferevent* bev = (struct bufferevent*)arg;
 //   printf("请输入要发送的数据: \n");
    read(fd, buf, sizeof(buf));
    bufferevent_write(bev, buf, strlen(buf)+1);
}


int main(int argc, const char* argv[])
{

    // init server 
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9876);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);

    struct event_base* base;
    base = event_base_new();
    // 创建套接字
    // 绑定
    // 接收连接请求
    struct evconnlistener* listener;
    listener = evconnlistener_new_bind(base, cb_listener, base, 
                                  LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 
                                  36, (struct sockaddr*)&serv, sizeof(serv));

								  
	
	
			
	
    event_base_dispatch(base);

    evconnlistener_free(listener);
    event_base_free(base);

    return 0;
}
//client.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/bufferevent.h>


void read_cb(struct bufferevent *bev, void *arg)
{
    char buf[1024] = {0}; 
    bufferevent_read(bev, buf, sizeof(buf));
    printf("Server say: %s\n", buf);
}

void write_cb(struct bufferevent *bev, void *arg)
{
   printf("我是写缓冲区的回调函数...您已发送\n"); 
}

void event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF)
    {
        printf("connection closed\n");  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
        printf("some other error\n");
    }
    else if(events & BEV_EVENT_CONNECTED)
    {
        printf("服务器已连接\n");
        return;
    }
    
    bufferevent_free(bev);
    printf("free bufferevent...\n");
}

void send_cb(evutil_socket_t fd, short what, void *arg)
{
    char buf[1024] = {0}; 
    struct bufferevent* bev = (struct bufferevent*)arg;
   // printf("请输入要发送的数据: \n");
    read(fd, buf, sizeof(buf));
    bufferevent_write(bev, buf, strlen(buf)+1);
}


int main(int argc, const char* argv[])
{
    struct event_base* base;
    base = event_base_new();


    struct bufferevent* bev;
    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);

    // 连接服务器
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9876);
    evutil_inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
    bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));

    // 设置回调
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
    bufferevent_enable(bev, EV_READ | EV_PERSIST);

    // 创建一个事件
    struct event* ev = event_new(base, STDIN_FILENO, 
                                 EV_READ | EV_PERSIST, 
                                 send_cb, bev);
    event_add(ev, NULL);
    
    event_base_dispatch(base);

    event_base_free(base);

    return 0;
}

更换你想要的服务器地址ip编译后就尽情享受聊天吧!!~

gcc server.c -o server -levent
gcc client.c -o client -levent

文中函数相关参数可翻阅libevent中文手册https://download.csdn.net/download/lemon_tea666/11247645

  • 19
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值