最近一直在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
