网络编程-libevnet不带缓冲的事件框架
1.1工作流程
图1 工作流程
常用的API:
分类 | API | 含义 |
事件 框架 | struct event_base* event_base_new(void); | 创建事件框架 |
event_base_free(struct event_base* base); | 释放事件框架 | |
const char *event_base_get_method(const struct event_base *base); | 获取支持的使用IO方法 | |
事件 | struct event *event_new(struct event_base *base, evutil_socket_t fd, shord what, event_callback_fn cb, void *arg); | 创建新事件: EV_READ:读 EV_WRITE:写 EV_SIGNAL:信号量EV_PERSIST:持续触发EV_ET://边沿模式 |
void event_free(struct event *event); | 释放事件 | |
int event_add( struct event *ev, const struct timeval *tv); | 设置未决事件 | |
int event_del(struct event *ev); | 设置已决事件 | |
event_base_dispatch(struct event_base* base) | 开始循环 | |
int event_base_loopexit( struct event_base *base, const struct timeval *tv); | 延迟终止循环(需等待本次事件完成) | |
int event_base_loopbreak(struct event_base *base); | 立即终止循环 |
先看效果:先开启服务器后开启服务器
这个例子简单的连接,取代传统的socket的编写网络编程,还有就是采用多路IO复用的方式来实现并发,此程序沒有进行优化,比如
- 当首先启动客户端程序的时候,将发生崩溃
- 当停止连接的时候,杀掉服务端,客户端将疯狂输出
- 没有使用的其他的事件服务来优化代码,比如定时器,信号量等,这个主要在带缓存的事件服务框架里
1.2客户端代码
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<event.h>
#include<event2/bufferevent.h>
#include<event2/buffer.h>
#include<event2/util.h>
typedef struct sockaddr SA;
typedef struct
{
char head[4];
unsigned long pack_len;
int code;
} msg_header;
char buf[50];
void read_cb(evutil_socket_t fd, short what, void *arg)
{
char buf[1024] = {0};
int len = read(fd, buf, sizeof(buf));
printf("say:%s\n", buf);
sleep(1);
}
int tcp_connect_server(const char* server_ip, int port)
{
int sockfd, status, save_errno;
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr) );
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
status = inet_aton(server_ip, &server_addr.sin_addr);
if( status == 0 )
{
errno = EINVAL;
return -1;
}
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if( sockfd == -1 )
return sockfd;
status = connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );
if( status == -1 )
{
save_errno = errno;
close(sockfd);
errno = save_errno;
return -1;
}
evutil_make_socket_nonblocking(sockfd);
return sockfd;
}
void tcp_send(int sockfd)
{
msg_header st1;
memcpy(st1.head, "kpr", 4);
st1.pack_len = 8;
st1.code = 0;
memcpy(buf, &st1, sizeof(msg_header));
memcpy(buf+sizeof(msg_header),"lishuwei",9);
printf("head:%s\n", st1.head);
printf("code:%d\n", st1.code);
printf("pack_len:%d\n", st1.pack_len);
printf("key:%s\n", buf+sizeof(msg_header));
if(-1==write(sockfd,buf,sizeof(buf)+1))
{
printf("socket write error");
}
}
int start_connct(char* server_ip, int port)
{
printf("msg_header111:%d",sizeof( msg_header));
//两个参数依次是服务器端的IP地址、端口号
int sockfd = tcp_connect_server(server_ip, port);
if( sockfd == -1)
{
perror("tcp_connect error ");
return -1;
}
printf("connect to server successful\n");
tcp_send(sockfd);
struct event_base* base = NULL;
base = event_base_new();
// 创建事件
struct event* ev = NULL;
ev = event_new(base, sockfd, EV_READ | EV_PERSIST, read_cb, NULL);
event_add(ev, NULL);
event_base_dispatch(base);
event_free(ev);
event_base_free(base);
close(sockfd);
printf("finished \n");
return 1;
}
int main(int argc,char* argv[])
{
start_connct(argv[1],atoi(argv[2]));
return 0;
}
1.3 服务器
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "arpa/inet.h"
#include "event.h"
#include <signal.h>
typedef struct sockaddr SA;
typedef struct
{
char head[4];
unsigned long pack_len;
int code;
} msg_header;
void accept_cb(int fd, short events, void* arg);
void socket_read_cb(struct bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);
int tcp_server_init(int port, int listen_num);
int main(int argc, char** argv)
{
int listener = tcp_server_init(9999, 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);
event_base_free(base);
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 = (struct event_base*)arg;
struct bufferevent* bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, socket_read_cb, NULL, event_cb, arg);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
}
void socket_read_cb(struct bufferevent* bev, void* arg)
{
msg_header st2;
char msg[4096];
size_t len = bufferevent_read(bev, msg, sizeof(msg));
memcpy(&st2, msg, sizeof(msg_header));
printf("socket_read_cb head:%s\n", st2.head);
printf("socket_read_cb code:%d\n", st2.code);
printf("socket_read_cb pack_len:%d\n", st2.pack_len);
printf("socket_read_cb key:%s\n", msg+sizeof(msg_header));
bufferevent_write(bev,msg,sizeof(msg_header)+st2.pack_len);
}
void event_cb(struct bufferevent *bev, short event, void *arg)
{
if (event & BEV_EVENT_EOF)
printf("connection closed\n");
else if (event & BEV_EVENT_ERROR)
printf("some other error\n");
bufferevent_free(bev);
}
int tcp_server_init(int port, int listen_num)
{
int errno_save;
evutil_socket_t 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, (SA*)&sin, sizeof(sin)) < 0 )
goto error;
if( listen(listener, listen_num) < 0)
goto error;
//跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener);
return listener;
error:
evutil_closesocket(listener);
return -1;
}
参考:
https://blog.gmem.cc/libevent-study-note
https://blog.csdn.net/weixin_44718794/article/details/107065921
先看效果:先开启服务器后开启服务器