使用bufferevent進行libevent服務端和客戶端的開發


參考了網上的一些例子,實驗了基於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的循環就返回了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM