參考了網上的一些例子,實驗了基於bufferevent的開發。
首先是服務端:
#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> using namespace std; void listener_cb(evconnlistener *listener, evutil_socket_t fd, sockaddr *sock, int socklen, void *arg); void socket_read_cb(bufferevent *bev, void *arg); void socket_event_cb(bufferevent *bev, short events, void *arg); int main(int argc, char **argv) { sockaddr_in sin; memset(&sin, 0, sizeof(sockaddr_in)); sin.sin_family = AF_INET; sin.sin_port = htons(8899); event_base *base = event_base_new(); evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, 10, (sockaddr*)&sin, sizeof(sockaddr_in)); event_base_dispatch(base); evconnlistener_free(listener); event_base_free(base); } void listener_cb(evconnlistener *listener, evutil_socket_t fd, sockaddr *sock, int socklen, void *arg) { printf("accept a client %d\n", fd); event_base *base = (event_base *) arg; 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(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(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); }
編譯命令:
g++ -o lserver lserver.cpp -I/usr/local/include -levent -L/usr/local/lib -Wl,-rpath=/usr/local/lib
2016.09.28
我把server和client編譯的命令,整理成了新的Makefile文件:
CXX=/opt/compiler/gcc-4.8.2/bin/g++ INCPATH= \ /home/work/.jumbo/include/ DEP_LDFLAGS= \ -L/home/work/.jumbo/lib/ DEP_LDLIBS= \ -levent \ -lpthread TARGET= lserver lclient all : $(TARGET) lserver : lserver.cc $(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS) lclient : lclient.cc $(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS) .PHONY : all clean clean : rm -rf $(TARGET)
然后是客戶端:
#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(bufferevent *bev, void *arg); void event_cb(bufferevent *bev, short event, void *arg); int main(int argc, char **argv) { if (argc < 3) { printf("please input IP and port\n"); return 1; } event_base *base = event_base_new(); bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); event *ev_cmd = event_new(base, STDIN_FILENO, EV_READ|EV_PERSIST, cmd_msg_cb, (void *)bev); event_add(ev_cmd, NULL); 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, (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.\n"); exit(1); } bufferevent *bev = (bufferevent *)arg; bufferevent_write(bev, msg, ret); } void server_msg_cb(bufferevent *bev, void *arg) { char msg[1024]; size_t len = bufferevent_read(bev, msg, sizeof(msg)-1); msg[len] = '\0'; printf("Recv %s from server.\n", msg); } void event_cb(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("Client has successfully cliented.\n"); return; } bufferevent_free(bev); // free event_cmd // need struct as event is defined as parameter struct event *ev = (struct event *)arg; event_free(ev); }
編譯命令:
g++ -o lclient lclient.cpp -I/usr/local/include -levent -L/usr/local/lib -Wl,-rpath=/usr/local/lib
運行服務器命令:
./lserver
運行客戶端命令:
./lclient 127.0.0.1 8899
多次交互之后的兩邊輸出結果為:
[server]$ ./lserver accept a client 7 Server read the data: aaa Server read the data: bbb Server read the data: ccc Server read the data: ddd Server read the data: eeeee
[client]$ ./lclient 127.0.0.1 8899 Client has successfully cliented. aaa Recv I have read your data. from server. bbb Recv I have read your data. from server. ccc Recv I have read your data. from server. ddd Recv I have read your data. from server. eeeee Recv I have read your data. from server.
如果先關閉客戶端(Ctrl-C,也就是SIGINT),服務器端會打印一條提示,但是仍然可以接受其他的請求。
[server]$ ./lserver accept a client 7 Server read the data: aaa Server read the data: bbb Server read the data: ccc Server read the data: ddd Server read the data: eeeee connection closed accept a client 7 Server read the data: ttt
如果先關閉服務器端(Ctrl-C,也就是SIGINT),客戶端也會關閉:
[client]$ ./lclient 127.0.0.1 8899 Client has successfully cliented. ttt Recv I have read your data. from server. Connection closed. Finished
這是因為在客戶端上關聯的event,包括socket的event(通過bufferevent_free關閉),和cmd的event(通過event_free關閉),都已經被關閉了。那么整個event_base_dispatch的循環就返回了。