• chat server libevent 重点


    libevent 库基于事件驱动( event-driven),高性能;轻量级,专注于网络,采用Reactor设计模型。

    libevent网络编程流程:

    server端:所有的客户连接通过一个buffer全局数据管理,读入的消息用全局消息数据管理。

    struct bufferevent* buffers[MAX_CONNECTION];
    int cur_con = 0;
    int msgs_len = 2;
    message_t* msgs;
    msgs = (message_t*)malloc(sizeof(message_t)*MAX_MESSAGE_TOTAL)

    main函数:

    struct sockaddr_in srv;
    memset(&srv,0,sizeof(srv));
    srv.sin_family = AF_INET;
    srv.sin_port = htons(10001);
    srv.sin_addr.s_addr = htonl(INADDR_ANY);
    
    struct event_base* base;
    struct evconnlistener* evlistener;
    base = event_base_new();
    
    //evconnlistener_new_bind作用:socket,bind,listen并accept
    evlistener
    = evconnlistener_new_bind(base,evlistener_cb,args,LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,16,(struct sockaddr*)&srv,sizeof(srv)); evconnlistener_set_error_cb(evlistener,evlistener_error_cb);//设置evlistener错误回调 event_base_dispatch(base); //进入事件循环

    evlistener_error_cb
    回调:当evlistener出现错误,则调用此回调
    void evlistener_error_cb(struct evconnlistener* listener,void *arg){
        print_err("listen errir ,exit");
        event_base_loopbreak(base);
    }

    evlistener_cb回调:当evlistener上有连接进入,则调用此回调。bufferevent_socket_new创建一个与客户端对话的buffer,在后面的通信中,就用此buffer进行数据的读写。
    void evlistener_cb(struct evconnlistener* evlistener, evutil_socket_t connect_fd, struct sockaddr* cli, int cli_len, void *arg)
    {
        struct bufferevent* buffer ;
        buffer = bufferevent_socket_new(base,connect_fd,BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
        //创建一个buffer,与客户端通信
       bufferevent_enable(buffer,EV_READ
    | EV_WRITE); //将buffer读写打开 bufferevent_setcb(buffer,read_cb,NULL,event_cb,arg);//设置buffer上的回调函数 }

    read_cb
    回调:当buffer上有数据时,调用此回调。bufferevent_read函数读取buffer。bufferevent_write函数向buffer中写入数据
    void read_cb(struct bufferevent* buffer,void* arg){
    
        message_t* msgs = arg;
        int i = 0;
        while(bufferevent_read(buffer,(msgs + msgs_len),sizeof(message_t)) != 0){
            if(msgs_len == MAX_MESSAGE_TOTAL - 1){
                memcpy(msgs + 2,msgs+(MAX_MESSAGE_TOTAL/2 + 2),sizeof(message_t)*(MAX_MESSAGE_TOTAL/2 - 2));
                msgs_len = (MAX_MESSAGE_TOTAL/2 - 1 ); 
            }
            msgs_len++;
            printf("current message cont = %d 
    ",msgs_len);
            for(i = 0; i < cur_con; i++){
                deliver_msg(buffers[i],(msgs + msgs_len - 1));
            //在此函数内调用bufferevent_write()函数 } } }

    event_cb
    当buffer上有事件产生时(错误发生:eof,error),调用此回调。释放资源,并断开客户端连接
    void event_cb(struct bufferevent* buffer,short event,void* arg){
        int i = 0;
        int n = 0;
        if(event && BEV_EVENT_EOF){
            printf("connection %d closed
    ",bufferevent_getfd(buffer));    
        }else if(event && BEV_EVENT_ERROR){
            print_err("got error on thin connection");    
        }
        if(event && (BEV_EVENT_EOF | BEV_EVENT_ERROR)){
            for(i = 0; i < cur_con;i++){
                if(buffers[i] == buffer){
                    for(n = i ; n < cur_con - 1; n++){
                        buffers[n] = buffers[n+1];    
                    }    
                    break;    
                }    
            }
            cur_con --;
            bufferevent_setcb(buffer,NULL,NULL,NULL,NULL);//关掉所有buffer上的事件
            bufferevent_free(buffer);    //free buffer,与客户端断开连接。
        }
    }

    client端:

    main函数:创建base,创建buffer,并与服务端连接,在buffer上祖册读事件,接收用户输入,将输入写入buffer,创建独立线程去运行事件派发函数。

    struct sockaddr_in srv;
    memset(&srv,0,sizeof(srv));
    srv.sin_port = htons(10001);
    srv.sin_family = AF_INET;
    inet_pton(AF_INET,argv[1],&srv.sin_addr);
    
    struct event_base* base;
    base = event_base_new();
    struct bufferevent* buffer;
    buffer = bufferevent_socket_new(base,-1,BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
    bufferevent_setcb(buffer,read_cb,NULL,NULL,NULL);
    bufferevent_enable(buffer, EV_READ | EV_WRITE);
    
    if(pthread_create(&thread_id,NULL,(void*)ev_dispatch,NULL) != 0){
            print_err("create thread failed");    
    }
            //处理客户输入
        printf("33[1;34mplease bind your chat name :33[0m
    ");
        scanf("%s",name);
        memcpy(msg.name,name,sizeof(name));
    
        printf("33[1;34minput your message (next) :33[0m
    ");
        while(1){
            //printf("please input your text
    ");
            scanf("%s",text);
            memcpy(msg.msg,text,sizeof(text));
            bufferevent_write(buffer,&msg,sizeof(msg)); //将客户消息写入buffer
        }

    ev_dispatch
    函数:进入事件循环(从服务端接收消息)
    void ev_dispatch(){
        event_base_dispatch(base);
    }

    read_cb函数:当buffer上有数据进入时,进行数据循环读取。并输出到客户终端。
    int read_n = 0;
    int cont_ = 1280;
    message_t msg_r ;
    void
    read_cb(struct bufferevent* buffer,void* arg){ //printf("flag "); while(1){ //printf("read_n = %d , cont_ = %d ",read_n,cont_); read_n = bufferevent_read(buffer,((char*)&msg_r) + (1280 - cont_),cont_ ); if(read_n == 0){ break; } //printf("read_n = %d ",read_n); cont_ = cont_ - read_n; //printf("cont_ = %d ",cont_); if(cont_ == 0){ printf("33[31m%s say : ",msg_r.name); printf("%s 33[0m ",msg_r.msg); cont_ = 1280; read_n = 0; } //printf("read_n = %d ",read_n); } }
  • 相关阅读:
    代理模式
    装饰模式
    策略模式
    简单工厂模式
    linux下进程相关操作
    散列表(哈希表)
    转载:最小生成树-Prim算法和Kruskal算法
    二叉排序树和平衡二叉树
    堆排序
    快速排序
  • 原文地址:https://www.cnblogs.com/Ccluck-tian/p/12433561.html
Copyright © 2020-2023  润新知