開源項目(庫)之libevent學習(二)

在上一篇博文中,我們只是稍加對libevent進行了學習,自己私下感覺好像好的多東西都沒有涉及,於是在這篇中我們就來真正地學習下libevent的一些API用法,廢話不說,直接上代碼,這段代碼也是別人的代碼,但是它很有針對性,所以在此列舉出,稍後會有針對性地修改,代碼如下:

#ifndef __ECHO_SERVER__H
#define __ECHO_SERVER__H
#include <event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <vector>
using namespace std;

#define EVENT_INIT {event_init();}
#define MAX_LISTEN 10
namespace zmyer
{
    struct client
    {
        client()
        {
            fd = -1;
            buf_ev = NULL;
        }
        int fd;
        struct bufferevent* buf_ev;
    };

    void buf_read_callback(struct bufferevent* in,void* arg)
    {
        struct evbuffer* evreturn;
        char* buffer;
        buffer = evbuffer_readline(in->input);
        if(NULL == buffer)
            return;
        evreturn = evbuffer_new();
        evbuffer_add_printf(evreturn,"you said:%s\n",buffer);
        bufferevent_write_buffer(in,evreturn);
        evbuffer_free(evreturn);
        free(buffer);
    }
    void buf_write_callback(struct bufferevent* out,void* arg)
    {

    }
    void buf_error_callback(struct bufferevent* in,short what,void* arg)
    {
        struct client* clit = (client*)arg;
        bufferevent_free(clit->buf_ev);
        close(clit->fd);
        free(clit);
    }


    static void setNonBlock(int sockfd)
    {
        int flags;
        flags = fcntl(sockfd,F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(sockfd,F_SETFL,flags);
    }

    static void accept_callback(int fd,short ev,void* arg)
    {
        int client_fd;
        struct sockaddr_in client_addr;
        socklen_t client_len;
        struct client* clit;

        client_fd = ::accept(fd,(struct sockaddr*)&client_addr,&client_len);
        if(client_fd < 0)
        {
            perror("accpet failed\n");
            return;
        }
        setNonBlock(client_fd);
        clit = (client*) calloc(1,sizeof(client));
        if(clit == NULL)
        {
            perror("calloc failed\n");
            return;
        }
        clit->fd = client_fd;
        clit->buf_ev = bufferevent_new(
             client_fd,
             buf_read_callback,
             buf_write_callback,
             buf_error_callback,
             (void*)clit);
        bufferevent_enable(clit->buf_ev,EV_READ | EV_PERSIST);
    }
    class EchoServer
    {
        public:
            EchoServer(const int port = 0):port(port){}
            ~EchoServer(){
                close(sockfd);
            }

            bool init()
            {
                sockfd = ::socket(AF_INET,SOCK_STREAM,0);
                if(-1 == sockfd)
                {
                    perror("create socket failed\n");
                    return false;
                }
                struct sockaddr_in server_addr;
                bzero(&server_addr,sizeof(server_addr));
                server_addr.sin_family = AF_INET;
                server_addr.sin_port = htons(port);
                server_addr.sin_addr.s_addr = htons(INADDR_ANY);

                if(bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
                {
                    perror("bind socket failed\n");
                    return false;
                }
                if(listen(sockfd,MAX_LISTEN)<0)
                {
                    perror("listen socket failed\n");
                    return false;
                }
                int reuse = 1;
                setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
                zmyer::setNonBlock(sockfd);
                struct event accept_event;
                event_set(&accept_event,sockfd,EV_READ | EV_PERSIST,accept_callback,NULL);
                event_add(&accept_event,NULL);
                event_dispatch();
            }
        private:
            int sockfd;
            int port;
    };
}
#endif

測試程序:

#include "EchoServer.h"
using namespace zmyer;
int main()
{
    EVENT_INIT;
    EchoServer echoServer(19999);
    echoServer.init();
    return 0;
}

測試結果:

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
you said:hello
yes
you said:yes
back
you said:back
Connection closed by foreign host.

從上面的代碼中可以看出,在libevent中有好多種對事件不同的處理方法,在上篇中,我們曾設計實現了一個機遇libevent的聊天室,那個聊天室只是是用來下libevent的事件處理機制,至於對於buffer的接收和發送,都是自己通過代碼實現的,總感覺有點不爽,今天就在這裏基於libevent再實現一遍那個聊天室,代碼如下:

#ifndef __ECHO_SERVER__H
#define __ECHO_SERVER__H
#include <event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <vector>
#include <iostream>
using namespace std;

#define EVENT_INIT {event_init();bufferEventVec.clear();}
#define MAX_LISTEN 10
namespace zmyer
{
    struct client
    {
        client()
        {
            fd = -1;
            buf_ev = NULL;
        }
        int fd;
        struct bufferevent* buf_ev;
    };

    vector<bufferevent*> bufferEventVec;
    typedef vector<bufferevent*>::iterator bufferEventVec_Iter;

    void display()
    {
        cout<<"bufferEventVec.size():"<<bufferEventVec.size()<<endl;
    }
    void bufferevent_remove(bufferevent* bufevent)
    {
        if(NULL == bufevent) return;
        for(bufferEventVec_Iter iter = bufferEventVec.begin(); iter != bufferEventVec.end();++iter)
        {
            if(*iter==bufevent)
            {
                bufferEventVec.erase(iter);
                break;
            }
        }
    }
    void buf_read_callback(struct bufferevent* in,void* arg)
    {
        struct evbuffer* evreturn;
        char* buffer;
        buffer = evbuffer_readline(in->input);
        if(NULL == buffer)
            return;

        printf("MSG:%s\n",buffer);
        evreturn = evbuffer_new();
        evbuffer_add_printf(evreturn,"%s\n",buffer);
        for(bufferEventVec_Iter iter = bufferEventVec.begin();iter!= bufferEventVec.end();++iter)
        {
            if(*iter == in) continue;
            bufferevent_write_buffer(*iter,evreturn);
        }
        evbuffer_free(evreturn);
        free(buffer);
    }
    void buf_write_callback(struct bufferevent* out,void* arg)
    {

    }
    void buf_error_callback(struct bufferevent* in,short what,void* arg)
    {
        struct client* clit = (client*)arg;
        bufferevent_free(clit->buf_ev);
        bufferevent_remove(clit->buf_ev);
        close(clit->fd);
        free(clit);
    }

    static void setNonBlock(int sockfd)
    {
        int flags;
        flags = fcntl(sockfd,F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(sockfd,F_SETFL,flags);
    }

    static void accept_callback(int fd,short ev,void* arg)
    {
        int client_fd;
        struct sockaddr_in client_addr;
        socklen_t client_len;
        struct client* clit;

        client_fd = ::accept(fd,(struct sockaddr*)&client_addr,&client_len);
        if(client_fd < 0)
        {
            perror("accpet failed\n");
            return;
        }
        setNonBlock(client_fd);
        clit = (client*) calloc(1,sizeof(client));
        if(clit == NULL)
        {
            perror("calloc failed\n");
            return;
        }
        clit->fd = client_fd;
        clit->buf_ev = bufferevent_new(
             client_fd,
             buf_read_callback,
             buf_write_callback,
             buf_error_callback,
             (void*)clit);
        bufferevent_enable(clit->buf_ev,EV_READ | EV_PERSIST);
        bufferEventVec.push_back(clit->buf_ev);
    }
    class ChatServer
    {
        public:
            ChatServer(const int port = 0):port(port){}
            ~ChatServer(){
                close(sockfd);
            }

            bool init()
            {
                sockfd = ::socket(AF_INET,SOCK_STREAM,0);
                if(-1 == sockfd)
                {
                    perror("create socket failed\n");
                    return false;
                }
                struct sockaddr_in server_addr;
                bzero(&server_addr,sizeof(server_addr));
                server_addr.sin_family = AF_INET;
                server_addr.sin_port = htons(port);
                server_addr.sin_addr.s_addr = htons(INADDR_ANY);

                if(bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
                {
                    perror("bind socket failed\n");
                    return false;
                }
                if(listen(sockfd,MAX_LISTEN)<0)
                {
                    perror("listen socket failed\n");
                    return false;
                }
                int reuse = 1;
                setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
                zmyer::setNonBlock(sockfd);
                struct event accept_event;
                event_set(&accept_event,sockfd,EV_READ | EV_PERSIST,accept_callback,NULL);
                event_add(&accept_event,NULL);
                event_dispatch();
            }
        private:
            int sockfd;
            int port;
    };
}
#endif

客戶端代碼:

#ifndef __LIBEVENT_SERVER__H
#define __LIBEVENT_SERVER__H
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <event.h>
#include <boost/smart_ptr.hpp>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fstream>
#include <iterator>
using namespace std;
using namespace boost;

#define MAX_CONN 10
#define MAX_BUFFERSIZE 1024

#define EVENT_INIT {event_init();}
namespace zmyer
{
    struct Info
    {
        Info()
        {
            fd= -1;
            clientname.clear();
        }
        Info(const Info& in)
        {
            clientname = in.clientname;
            fd = in.fd;
        }
        Info(const Info* in)
        {
            clientname = in->clientname;
            fd = in->fd;
        }
        Info& operator = (const Info& in)
        {
            if(this == &in)
                return *this;
            clientname = in.clientname;
            fd = in.fd;
            return *this;

        }
        ~Info(){}

        string clientname;
        int fd;
    };

    struct Msg
    {
        Msg()
        {
            clientname.clear();
            msg.clear();
        }
        Msg(const Msg& ms)
        {
            clientname = ms.clientname;
            msg = ms.msg;
        }
        Msg& operator = (const Msg& ms)
        {
            clientname = ms.clientname;
            msg = ms.msg;
        }
        ~Msg(){}

        void setClientName(string clientname)
        {
            this->clientname = clientname;
        }
        void setMsg(string msg)
        {
            this->msg = msg;
        }
        string serialize()
        {
            string tag = "#";
            return (clientname + tag + msg +"\n");
        }
        void unserialize(string message)
        {
            string tag = "#";
            string::size_type pos = message.find(tag);
            if(pos == string::npos)
            {
                clientname ="";
                msg = "";
            }
            else
            {
                clientname.assign(message,0,pos);
                msg.assign(message,pos+1,(message.length() - pos));
            }
        }

        string clientname;
        string msg;
    };

    static void* send(void* arg)
    {
        shared_ptr<Info> p(new Info((Info*)arg));
        string line,buffer;
        for(;;)
        {
            line.clear();
            buffer.clear();
            getline(cin,line);
            Msg msg;
            msg.clientname = p->clientname;
            msg.msg = line;
            buffer = msg.serialize();
            ::send(p->fd,buffer.c_str(),strlen(buffer.c_str()),0);
        }
        return NULL;
    }
    static void* recv(void* arg)
    {
        shared_ptr<Info> p(new Info((Info*)arg));
        char buffer[MAX_BUFFERSIZE];
        for(;;)
        {
            bzero(buffer,sizeof(buffer));
            int readlen = ::recv(p->fd,buffer,sizeof(buffer),0);
            if(readlen <=0)
                continue;
            Msg msg;
            msg.unserialize(buffer);
            if(msg.clientname !="")
            {
                printf("%s : %s\n",msg.clientname.c_str(),msg.msg.c_str());
            }
        }
        return NULL;
    }

    class libeventClient
    {
        public:
            libeventClient(string ip = string(),string clientname = string(),const int port = 0):ip(ip),clientname
        {
            sock = -1;
        }
            ~libeventClient(){}
            bool init()
            {
                sock = ::socket(AF_INET,SOCK_STREAM,0);
                if(-1 == sock)
                {
                    printf("sock init failed\n");
                    return false;
                }
                struct sockaddr_in client_addr;
                bzero(&client_addr,sizeof(client_addr));
                client_addr.sin_family = AF_INET;
                client_addr.sin_port = htons(0);
                client_addr.sin_addr.s_addr = htons(INADDR_ANY);
                if(bind(sock,(struct sockaddr*)&client_addr,sizeof(client_addr))<0)
                {
                    printf("bind the address failed\n");
                    return false;
                }

                struct sockaddr_in server_addr;
                bzero(&server_addr,sizeof(server_addr));
                server_addr.sin_family = AF_INET;
                server_addr.sin_port = htons(port);
                inet_aton(ip.c_str(),&(server_addr.sin_addr));
                if(::connect(sock,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
                {
                    printf("connect to libeventserver failed\n");
                    return false;
                }
                return true;
            }

            void startSendThread()
            {
                Info in;
                in.fd = sock;
                in.clientname = clientname;
                pthread_create(&sendThread,NULL,&send,&in);
            }
            void startRecvThread()
            {
                Info in;
                in.fd = sock;
                in.clientname = clientname;
                pthread_create(&recvThread,NULL,&recv,&in);
            }
            void run()
            {
                startSendThread();
                startRecvThread();
            }
            void stop()
            {
                pthread_join(sendThread,NULL);
                pthread_join(recvThread,NULL);
            }
        private:
            string ip;
            string clientname;
            int port;
            int sock;
            pthread_t recvThread,sendThread;
    };
}
#endif

服務端測試程序:

#include "EchoServer.h"
using namespace zmyer;
int main()
{
    EVENT_INIT;
    ChatServer chatServer(19999);
    chatServer.init();
    return 0;
}

客戶端測試程序:

#include "libeventServer.h"
using namespace zmyer;

int main(int argc,char** argv)
{
    if(argc < 2)
    {
        printf("usage:%s <clientname>\n",argv[0]);
        return 0;
    }
    libeventClient libClient("127.0.0.1",argv[1],19999);
    if(!libClient.init())
    {
        printf("libClient init failed\n");
        return 0;
    }
    libClient.run();
    sleep(60);
    libClient.stop();
    return 0;
}

測試結果:

服務器端:

MSG:B#hello,everyone,my name is B
MSG:A#weclome B,my name is A
MSG:A#how wonderful today is !!!!
MSG:B#year..,I perfer to walking,how about you A
^C
客戶端A:


B : hello,everyone,my name is B

weclome B,my name is A
how wonderful today is !!!!
B : year..,I perfer to walking,how about you A

^C

客戶端B:

hello,everyone,my name is B
A : weclome B,my name is A

A : how wonderful today is !!!!

year..,I perfer to walking,how about you A
^C

總結

        本篇博文主要是針對libevent庫又重新將我們的聊天室重新寫了一篇,這一遍服務器端代碼從整體上看要好於前幾次,這次所使用的就是libevent庫的一些接口函數,而客戶端基本上和之前一樣,我這樣寫主要是爲了保持代碼的完整性,在客戶端方面我們需要注意一點就是libevent的evbuffer_readline函數只能讀取\r\n,\r,\n等結尾的字符串,所以客戶端在發送數據時需要帶上至少一個類似的結尾,好了,本篇博文到此結束,後面我們將會以libevent庫再來實現幾個應用實例。

如果需要,請註明轉載,多謝

發佈了52 篇原創文章 · 獲贊 10 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章