最近一直在ubuntu下研究c++开源代码,本文的内容是大名鼎鼎的libevent库。
本文将从3个例子着手,从简单到复杂,分别包含了client与server。
文章参考该文 ,并对其中的代码进行重新梳理。本文代码均在ubuntu下通过程序验证。
1.初级版
>gcc -o client client.c -levent
>gcc -o server server.c -levent
上述两句命令能够编译出2个可执行文件。-levent在这里起到的作用是,链接libevent库到程序中。
此程序中server默认使用9999端口,且client运行时需要指定ip和port,如>./client 127.0.0.1 9999
client.c如下:
#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/util.h>
int tcp_connect_server(const char* server_ip,int port);
void cmd_msg_cb(int fd,short events,void*arg);
void socket_read_cb(int fd,short events,void*arg);
int
main(int argc,char**argv){
if(argc<3){
printf("please input 2 parameter\n");
return -1;
}
//2 params, IP, port
int sockfd=tcp_connect_server(argv[1],atoi(argv[2]));
if(sockfd==-1){
perror("tcp_connect error\n");
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);
//监听终端输入事件
struct event* ev_cmd=event_new(base,STDIN_FILENO,EV_READ|EV_PERSIST,cmd_msg_cb,(void*)&sockfd);
event_add(ev_cmd,NULL);
//
event_base_dispatch(base);
printf("finished!\n");
return 0;
}
/*
* callback,
*/
void socket_read_cb(int fd,short events,void*arg){
char msg[1024];
//
int len=read(fd,msg,sizeof(msg)-1);
if(len<=0){
perror("read fail\n");
exit(1);
}
msg[len]='\0';
printf("recv %s from server\n",msg);
}
/*
* callback,send msg to server
*/
void cmd_msg_cb(int fd,short events,void*arg){
char msg[1024];
int ret=read(fd,msg,sizeof(msg));
if(ret<=0){
perror("read fail\n");
exit(1);
}
int sockfd=*((int*)arg);
//把终端的消息发送给服务器
//为了简单起见,不考虑写一半数据的情况
write(sockfd,msg,ret);
}
int tcp_connect_server(const char* server_ip,int port){
int sockfd,status,save_erro;
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,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(status==-1){
int save_errno=errno;
close(sockfd);
errno=save_errno;
return -1;
}
evutil_make_socket_nonblocking(sockfd);
return sockfd;
}
server.c如下:
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<event.h>
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) {
int listener = tcp_server_init(9999, 10);
if (-1==listener) {
perror("tcp_server_init error\n");
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);
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;
//动态创建一个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 = read(fd, msg, sizeof(msg) - 1);
if (len <= 0) {
printf("some error happen when read\n");
event_free(ev);
close(fd);
return;
}
msg[len] = '\0';
printf("recv the client(%d) msg: %s\n",fd, msg);
char reply_msg[4096] = "I have received the msg: ";
strcat(reply_msg + strlen(reply_msg), msg);
write(fd, reply_msg, strlen(reply_msg));
}
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, (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;
}
2.中级版
和上述版本类似,编译过程相同。
client.c
#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>
int tcp_connect_server(const char*server_ip, int port);
void cmd_msg_cb(int fd, short events, void*arg);
void server_msg_cb(struct bufferevent*bev, void*arg);
void event_cb(struct bufferevent*bev, short event, void*arg);
int main(int argc, char**argv) {
if (argc < 3) {
printf("please input 2 parameter\n");
return -1;
}
//2 param
int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));
if (sockfd == -1) {
perror("tcp_connect error ");
return -1;
}
printf("connect to server successful\n");
struct event_base* base = event_base_new();
struct bufferevent* bev = bufferevent_socket_new(base, sockfd,
BEV_OPT_CLOSE_ON_FREE);
//监听终端输入事件
struct event* ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,
cmd_msg_cb, (void*) bev);
event_add(ev_cmd, NULL);
//当socket关闭时会用到回调函数
bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*) ev_cmd);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
event_base_dispatch(base);
printf("finished \n");
return 0;
}
void cmd_msg_cb(int fd, short events, void*arg) {
char msg[1024];
int ret = read(fd, msg, sizeof(msg));
if (ret < 0) {
perror("read fail ");
exit(1);
}
struct bufferevent* bev = (struct bufferevent*) arg;
//把终端的消息发送给服务器端
bufferevent_write(bev, msg, ret);
}
void server_msg_cb(struct bufferevent* bev, void*arg) {
char msg[1024];
size_t len = bufferevent_read(bev, msg, sizeof(msg));
msg[len] = '\0';
printf("recv %s from server\n", msg);
}
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);
//因此,socket没有了,event也就没有存在必要了
struct event*ev = (struct event*) arg;
event_free(ev);
}
int tcp_connect_server(const char*server_ip, int port) {
int sockfd, status, 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,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(status==-1)
{
int save_errno=errno;
close(sockfd);
errno=save_errno;
return -1;
}
evutil_make_socket_nonblocking(sockfd);
return sockfd;
}
server.c
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<event.h>
#include<event2/bufferevent.h>
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) {
char msg[4096];
size_t len = bufferevent_read(bev, msg, sizeof(msg));
msg[len] = '\0';
printf("recv the client msg: %s\n", msg);
char reply_msg[4096] = "I have received the msg: ";
strcat(reply_msg + strlen(reply_msg), msg);
bufferevent_write(bev, reply_msg, strlen(reply_msg));
}
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;
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,(struct sockaddr*)&sin,sizeof(sin))<0) {
int errno_save=errno;
evutil_closesocket(listener);
errno=errno_save;
return -1;
}
if(listen(listener,listen_num)<0)
{
int errno_save=errno;
evutil_closesocket(listener);
errno=errno_save;
return -1;
}
evutil_make_socket_nonblocking(listener);
return listener;
}
3.高级版
在第三部分的代码中,有很多地方存在错误。主要存在与server的头文件引用和内部struct调用上。
目前所有的文档资料中,第三部分代码均很类似,且无法编译成功,本人也是修改了部分代码,可正常通过gcc编译和运行。
代码如下所示:
client.c
#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>
int tcp_connect_server(const char*server_ip, int port);
void cmd_msg_cb(int fd, short events, void*arg);
void server_msg_cb(struct bufferevent*bev, void*arg);
void event_cb(struct bufferevent*bev, short event, void*arg);
int main(int argc, char**argv) {
if (argc < 3) {
//2param
printf("please input 2 parameter\n");
return -1;
}
struct event_base*base = event_base_new();
struct bufferevent*bev = bufferevent_socket_new(base, -1,
BEV_OPT_CLOSE_ON_FREE);
//监听终端输入事件
struct event*ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,
cmd_msg_cb, (void*) bev);
event_add(ev_cmd, NULL);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &server_addr.sin_addr);
bufferevent_socket_connect(bev, (struct sockaddr*) &server_addr,
sizeof(server_addr));
bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*) ev_cmd);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
event_base_dispatch(base);
printf("finished \n");
return 0;
}
void cmd_msg_cb(int fd, short events, void*arg) {
char msg[1024];
int ret = read(fd, msg, sizeof(msg));
if (ret < 0) {
perror("read fail ");
exit(1);
}
struct bufferevent*bev = (struct bufferevent*) arg;
//把终端的消息发送给服务器端
bufferevent_write(bev, msg, ret);
}
void server_msg_cb(struct bufferevent*bev, void*arg) {
char msg[1024];
size_t len = bufferevent_read(bev, msg, sizeof(msg));
msg[len] = '\0';
printf("recv %s from server \n", msg);
}
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");
} else if (event & BEV_EVENT_CONNECTED) {
printf("the client has connected to server\n");
return;
}
//自动关闭套接字和清空缓冲区
bufferevent_free(bev);
struct event*ev = (struct event*) arg;
event_free(ev);
}
server.c
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<event.h>
#include<event2/listener.h>
#include<event2/bufferevent.h>
#include<event2/thread.h>
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr*sock, int socklen, void*arg);
void socket_read_cb(struct bufferevent*bev, void*arg);
void socket_event_cb(struct bufferevent*bev, short events, void*arg);
int main() {
//evthread_use_pthreads();//enable threads
struct sockaddr_in sin;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(9999);
struct event_base*base = event_base_new();
struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, 10,
(struct sockaddr*) &sin, sizeof(struct sockaddr_in));
//事件循环开始
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
/*
* 当新客户端连接上服务器,此函数被调用,libevent已经帮助accept了此客户端,该客户端的文件描述符位fd
*/
void listener_cb(struct evconnlistener*listener, evutil_socket_t fd,
struct sockaddr*sock, int socklen, void*arg) {
printf("accept a client %d\n", fd);
struct event_base*base = (struct event_base*) arg;
//为此客户端分配一个bufferevent
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, NULL);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
}
void socket_read_cb(struct bufferevent*bev, void*arg) {
char msg[4096];
size_t len = bufferevent_read(bev, msg, sizeof(msg) - 1);
msg[len] = '\0';
printf("server read the data %s\n", msg);
char reply[] = "I have read your data";
bufferevent_write(bev, reply, strlen(reply));
}
void socket_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);
}
Jetbrains,Avaliable License Servers
http://xidea.online/servers.html