libevent學習之三:簡單的伺服器和客戶端
阿新 • • 發佈:2019-01-05
1.伺服器
#include <stdio.h> #include <time.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #include <event2/listener.h> #include <event2/util.h> #include <event2/event.h> const int PORT = 2500; const int BUFFER_SIZE = 1024; void listener_cb(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *); void conn_writecb(struct bufferevent *, void *); void conn_readcb(struct bufferevent *, void *); void conn_eventcb(struct bufferevent *, short, void *); void delay(int ms); int main(int argc, char **argv) { printf("I am server\n"); #ifdef WIN32 WSAData wsaData; WSAStartup(MAKEWORD(2, 0), &wsaData); #endif struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(PORT); struct evconnlistener *listener; struct event_base *base = event_base_new(); if (!base) { printf("Could not initialize libevent\n"); return 1; } listener = evconnlistener_new_bind(base, listener_cb, (void *)base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr*)&sin, sizeof(sin)); if (!listener) { printf("Could not create a listener\n"); return 1; } event_base_dispatch(base); evconnlistener_free(listener); event_base_free(base); printf("done\n"); return 0; } void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data) { printf("Detect an connection\n"); struct event_base *base = (struct event_base *)user_data; struct bufferevent *bev; bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); if (!bev) { printf("Could not create a bufferevent\n"); event_base_loopbreak(base); return; } bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL); bufferevent_enable(bev, EV_READ|EV_WRITE); //伺服器監聽到連線時,給客戶端傳送第一條訊息 char *reply_msg = "I receive a message from server"; bufferevent_write(bev, reply_msg, strlen(reply_msg)); } //conn_writecwritecb函式將在bufferevent中的output evbuffer緩衝區傳送完成後被呼叫。 //此時evbuffer_get_length(output) = 0,說明output evbuffer緩衝區被清空。 //假設發現有10000條記錄要傳送出去,1次傳送10000條將佔用大量記憶體,所以,我們要分批發送 //先發送100條資料,假設每條資料為1024位元組bufferevent_write(bev,buf,1024 *100); //系統在這100條記錄傳送完成後將呼叫conn_writecbb回撥函式,然後在該函式中迴圈傳送剩下的 //資料 void conn_writecb(struct bufferevent *bev, void *user_data) { // struct evbuffer *output = bufferevent_get_output(bev); // if (evbuffer_get_length(output) == 0) // { // printf("Output evbuffer is flushed\n"); // bufferevent_free(bev); // } //delay 1 second delay(1000); static int msg_num = 1; char reply_msg[1000] = {'\0'}; char *str = "I receive a message from server "; memcpy(reply_msg,str,strlen(str)); sprintf(reply_msg+strlen(str),"%d",msg_num); bufferevent_write(bev, reply_msg, strlen(reply_msg)); msg_num++; } void conn_readcb(struct bufferevent *bev, void *user_data) { struct evbuffer *input =bufferevent_get_input(bev); size_t sz=evbuffer_get_length(input); if (sz > 0) { char msg[BUFFER_SIZE] = {'\0'}; bufferevent_read(bev, msg, sz); printf("%s\n", msg); } } void conn_eventcb(struct bufferevent *bev, short events, void *user_data) { if (events & BEV_EVENT_EOF) { printf("Connection closed\n"); } else if (events & BEV_EVENT_ERROR) { printf("Got an error on the connection: %s\n",strerror(errno)); } bufferevent_free(bev); } void delay(int ms) { clock_t start = clock(); while(clock() - start < ms); }
2.客戶端
#include <stdio.h> #include <time.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #include <event2/listener.h> #include <event2/util.h> #include <event2/event.h> const int PORT = 2500; const int BUFFER_SIZE = 1024; void conn_writecb(struct bufferevent *, void *); void conn_readcb(struct bufferevent *, void *); void conn_eventcb(struct bufferevent *, short, void *); void delay(int ms); int main() { printf("I am client\n"); #ifdef WIN32 WSAData wsaData; WSAStartup(MAKEWORD(2, 0), &wsaData); #endif struct sockaddr_in srv; memset(&srv, 0, sizeof(srv)); srv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); srv.sin_family = AF_INET; srv.sin_port = htons(PORT); struct event_base *base = event_base_new(); if (!base) { printf("Could not initialize libevent\n"); return 1; } struct bufferevent* bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL); int flag=bufferevent_socket_connect(bev, (struct sockaddr *)&srv,sizeof(srv)); bufferevent_enable(bev, EV_READ | EV_WRITE); if(-1==flag) { printf("Connect failed\n"); return 1; } event_base_dispatch(base); event_base_free(base); printf("done\n"); return 0; } //conn_writecwritecb函式將在bufferevent中的output evbuffer緩衝區傳送完成後被呼叫。 //此時evbuffer_get_length(output) = 0,說明output evbuffer緩衝區被清空。 //假設發現有10000條記錄要傳送出去,1次傳送10000條將佔用大量記憶體,所以,我們要分批發送 //先發送100條資料,假設每條資料為1024位元組bufferevent_write(bev,buf,1024 *100); //系統在這100條記錄傳送完成後將呼叫conn_writecbb回撥函式,然後在該函式中迴圈傳送剩下的 //資料 void conn_writecb(struct bufferevent *bev, void *user_data) { // struct evbuffer *output = bufferevent_get_output(bev); // if (evbuffer_get_length(output) == 0) // { // printf("Output evbuffer is flushed\n"); // bufferevent_free(bev); // } //delay 1 second delay(1000); static int msg_num = 1; char reply_msg[1000] = {'\0'}; char *str = "I receive a message from client "; memcpy(reply_msg,str,strlen(str)); sprintf(reply_msg+strlen(str),"%d",msg_num); bufferevent_write(bev, reply_msg, strlen(reply_msg)); msg_num++; } void conn_readcb(struct bufferevent *bev, void *user_data) { struct evbuffer *input =bufferevent_get_input(bev); size_t sz=evbuffer_get_length(input); if (sz > 0) { char msg[BUFFER_SIZE] = {'\0'}; bufferevent_read(bev, msg, sz); printf("%s\n", msg); } } void conn_eventcb(struct bufferevent *bev, short events, void *user_data) { if (events & BEV_EVENT_EOF) { printf("Connection closed\n"); } else if (events & BEV_EVENT_ERROR) { printf("Got an error on the connection: %s\n",strerror(errno)); } else if( events & BEV_EVENT_CONNECTED) { printf("Connect succeed\n"); //客戶端連結成功後,給伺服器傳送第一條訊息 char *reply_msg = "I receive a message from client"; bufferevent_write(bev, reply_msg, strlen(reply_msg)); return ; } bufferevent_free(bev); } void delay(int ms) { clock_t start = clock(); while(clock() - start < ms); }
3.執行效果
先啟動伺服器,然後啟動客戶端,效果如下圖所示。
伺服器
客戶端
4.原始碼
原始碼預設使用的是MingW版本的libevent標頭檔案和庫,也包含Windows版本的libevent標頭檔案和庫,字尾為_win。