libevent

忘了在哪找的了,只是記錄了源碼。算法

bufferevent.cpp編程

/*************************************************
 * 本代碼使用libevent鏈接、登陸遠程POP3郵件服務器.
 */
#include <event2/event.h>
#include <event2/util.h>
#include <event2/bufferevent.h>
  
// 根據ip和端口生成sockaddr結構並返回其地址,同時輸出addrlen(實現略)
struct sockaddr * make_sock_addr(const char *ip, int port, int *addrlen);
  
// socket讀取事件回調函數,每收到一條遠程消息,該函數將會被調用一次。
void bufferevent_read_callback(struct bufferevent *bufevt, void *arg)
{
    static int i = 0;
    char buf[1024];
    // 從bufferevent中,讀取接收到的消息並打印
    size_t sz = bufferevent_read(bufevt, buf, 1024);
    printf("received bytes: %d\n", sz);
    if ( sz > 0 ) {
        printf("received: %s\n", buf);
    }
  
    // 按順序發送pop3指令
    if ( i == 0 ) bufferevent_write(bufevt, "user user1\n", 11);
    else if ( i == 1 ) bufferevent_write(bufevt, "pass 12345678\n", 14);
    else if ( i == 2) bufferevent_write(bufevt, "list\n", 5);
    else bufferevent_disable(bufevt, EV_READ);
    i++;
}
  
int main(int argc, char **argv)
{
    int addrlen = 0;
    struct sockaddr * paddr = make_sock_addr("10.1.1.201", 110, &addrlen);
  
    int fd = socket(AF_INET, SOCK_STREAM, 0);
  
    // 建立event base並綁定bufferevent
    struct event_base * evbase = event_base_new();
    struct bufferevent * bufevt = bufferevent_socket_new(evbase, fd, 0);
  
    // 設置sock讀取事件回調函數(bufferevent_read_callback),
    // 寫回調和錯誤回調暫不設置, 並開啓讀取事件通知開關。
    bufferevent_setcb(bufevt, bufferevent_read_callback, NULL, NULL, NULL);
    bufferevent_enable(bufevt, EV_READ);
  
    // 鏈接服務器, 並啓動事件循環
    bufferevent_socket_connect(bufevt, paddr, addrlen);
    event_base_dispatch(evbase);
  
    if ( bufevt )  bufferevent_free(bufevt);
    if ( evbase )  event_base_free(evbase);
    if ( fd >= 0 ) { close(fd); }
    return 0;
}

socket.cpp服務器

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>



static short ListenPort = 8080;
static long ListenAddr = INADDR_ANY;//任意地址的值就是0
static int MaxConnections = 1024;
static int ServerSocket;
static struct event ServerEvent;//建立event


//不論在什麼平臺編寫網絡程序,都應該使用NONBLOCK將一個socket設置成非阻塞模式。這樣能夠保證你的程序至少不會在recv/send/accept/connect這些操做上發生block從而將整個網絡服務都停下來
int SetNonblock(int fd)
{
    int flags;
    
    if ((flags = fcntl(fd, F_GETFL)) == -1) { //用來操做文件描述符的一些特性
        return -1;
    }

    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        return -1;
    }

    return 0;
}

//這個函數當客戶端的socket可讀時由libevent調用
void ServerRead(int fd, short ev, void *arg)
{
    struct client *client = (struct client *)arg;
    u_char buf[8196];
    int len, wlen;

    //會把參數fd 所指的文件傳送count個字節到buf指針所指的內存中
    len = read(fd, buf, sizeof(buf));
    if (len == 0) {
        /* 客戶端斷開鏈接,在這裏移除讀事件而且釋放客戶數據結構 */
        printf("disconnected\n");
        close(fd);
        event_del(&ServerEvent);
        free(client);
        return;
    } else if (len < 0) {
        /* 出現了其它的錯誤,在這裏關閉socket,移除事件而且釋放客戶數據結構 */
        printf("socket fail %s\n", strerror(errno));
        close(fd);
        event_del(&ServerEvent);
        free(client);
        return;
    }
    /* 
     爲了簡便,咱們直接將數據寫回到客戶端。一般咱們不能在非阻塞的應用程序中這麼作,
       咱們應該將數據放到隊列中,等待可寫事件的時候再寫回客戶端。 
     若是使用多個終端進行socket鏈接會出現錯誤socket fail Bad file descriptor
     */
    wlen = write(fd, buf, len);
    if (wlen < len) {
        printf("not all data write back to client\n");
    }

    return;
}

/*
   當有一個鏈接請求準備被接受時,這個函數將被libevent調用並傳遞給三個變量: 
   int fd:觸發事件的文件描述符. 
   short event:觸發事件的類型EV_TIMEOUT,EV_SIGNAL, EV_READ, or EV_WRITE. 
   void* :由arg參數指定的變量. 
*/
void ServerAccept(int fd, short ev, void *arg)
{
    int cfd;
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    int yes = 1;
    int retval;

    //將從鏈接請求隊列中得到鏈接信息,建立新的套接字,並返回該套接字的文件描述符。
    //新建立的套接字用於服務器與客戶機的通訊,而原來的套接字仍然處於監聽狀態。
    //該函數的第一個參數指定處於監聽狀態的流套接字
    cfd = accept(fd, (struct sockaddr *)&addr, &addrlen);
    if (cfd == -1) {
        printf("accept(): can not accept client connection");
        return;
    }
    if (SetNonblock(cfd) == -1) {
        close(cfd);
        return;
    }

    //設置與某個套接字關聯的選項
    //參數二 IPPROTO_TCP:TCP選項
    //參數三 TCP_NODELAY 不使用Nagle算法 選擇當即發送數據而不是等待產生更多的數據而後再一次發送
    // 更多參數TCP_NODELAY 和 TCP_CORK
    //參數四 新選項TCP_NODELAY的值
    if (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
        printf("setsockopt(): TCP_NODELAY %s\n", strerror(errno));
        close(cfd);
        return;
    }

    event_set(&ServerEvent, cfd, EV_READ | EV_PERSIST, ServerRead, NULL);
    event_add(&ServerEvent, NULL);
    
    printf("Accepted connection from %s\n", inet_ntoa(addr.sin_addr));
}

int NewSocket(void)
{
    struct sockaddr_in sa;

    //socket函數來建立一個可以進行網絡通訊的套接字。
    //第一個參數指定應用程序使用的通訊協議的協議族,對於TCP/IP協議族,該參數置AF_INET;
    //第二個參數指定要建立的套接字類型
    //流套接字類型爲SOCK_STREAM、數據報套接字類型爲SOCK_DGRAM、原始套接字SOCK_RAW
    //第三個參數指定應用程序所使用的通訊協議。
    ServerSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (ServerSocket == -1) {
        printf("socket(): can not create server socket\n");
        return -1;
    }

    if (SetNonblock(ServerSocket) == -1) {
        return -1;
    }

    //清空內存數據
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    //htons將一個無符號短整型數值轉換爲網絡字節序
    sa.sin_port = htons(ListenPort);
    //htonl將主機的無符號長整形數轉換成網絡字節順序
    sa.sin_addr.s_addr = htonl(ListenAddr);

    //(struct sockaddr*)&sa將sa強制轉換爲sockaddr類型的指針
    /*struct sockaddr 
        數據結構用作bind、connect、recvfrom、sendto等函數的參數,指明地址信息。
        但通常編程中並不直接針對此數據結構操做,而是使用另外一個與sockaddr等價的數據結構 struct sockaddr_in
        sockaddr_in和sockaddr是並列的結構,指向sockaddr_in的結構體的指針也能夠指向
        sockadd的結構體,並代替它。也就是說,你可使用sockaddr_in創建你所須要的信息,
        在最後用進行類型轉換就能夠了
    */
    //bind函數用於將套接字綁定到一個已知的地址上
    if (bind(ServerSocket, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
        close(ServerSocket);
        printf("bind(): can not bind server socket");
        return -1;
    }
    
    //執行listen 以後套接字進入被動模式
    //MaxConnections 鏈接請求隊列的最大長度,隊列滿了之後,將拒絕新的鏈接請求
    if (listen(ServerSocket, MaxConnections) == -1) {
        printf("listen(): can not listen server socket");
        close(ServerSocket);
        return -1;
    }

    /*
     event_set的參數:
     + 參數1: 爲要建立的event
     + 參數2: file descriptor,建立純計時器能夠設置其爲-1,即宏evtimer_set定義的那樣
     + 參數3: 設置event種類,經常使用的EV_READ, EV_WRITE, EV_PERSIST, EV_SIGNAL, EV_TIMEOUT,純計時器設置該參數爲0
     + 參數4: event被激活以後觸發的callback函數
     + 參數5: 傳遞給callback函數的參數
     備註:
            若是初始化event的時候設置其爲persistent的(設置了EV_PERSIST),
            則使用event_add將其添加到偵聽事件集合後(pending狀態),
            該event會持續保持pending狀態,即該event能夠無限次參加libevent的事件偵聽。
            每當其被激活觸發callback函數執行以後,該event自動從active轉回爲pending狀態,
            繼續參加libevent的偵聽(當激活條件知足,又能夠繼續執行其callback)。
            除非在代碼中使用event_del()函數將該event從libevent的偵聽事件集合中刪除。
            若是不經過設置EV_PERSIST使得event是persistent的,須要在event的callback中再次調用event_add
            (即在每次pending變爲active以後,在callback中再將其設置爲pending)
     */
    event_set(&ServerEvent, ServerSocket, EV_READ | EV_PERSIST, ServerAccept, NULL);
    //將event添加到libevent偵聽的事件集中
    if (event_add(&ServerEvent, 0) == -1) {
        printf("event_add(): can not add accept event into libevent");
        close(ServerSocket);
        return -1;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    int retval;
    
    event_init(); //初始化event base使用默認的全局current_base
    
    retval = NewSocket();
    if (retval == -1) {
        exit(-1);
    }
    
    event_dispatch(); //啓動事件隊列系統,開始監聽(並接受)請求
    
    return 0;
}

timer.cpp網絡

/ gcc timer.c -o timer -levent
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <event2/event.h>
#include <event2/event_struct.h>

#define N 300
#define BUFLEN 256

struct timeval lasttime;
struct ST_EventWithDescription
{
    struct event *p_event;
    int time_interval;
    char lable[BUFLEN];
};

static void timeout_cb(evutil_socket_t fd, short event, void *arg)
{
    struct timeval newtime, difference;
    struct ST_EventWithDescription *pSTEvent = arg;
    struct event *timeout = pSTEvent->p_event;
    double elapsed;

    evutil_gettimeofday(&newtime, NULL);
    evutil_timersub(&newtime, &lasttime, &difference);
    elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6);

    printf("%s called at %d: %.3f seconds since my last work.\n",
          (char*)pSTEvent->lable,(int)newtime.tv_sec, elapsed);
    lasttime = newtime;

    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = pSTEvent->time_interval;
    event_add(timeout, &tv);
}

void setParam(struct ST_EventWithDescription *stEventDescription,
              struct event *m_event,int time_interval,char* m_lable)
{
    stEventDescription->p_event = m_event;
    stEventDescription->time_interval = time_interval;
    memset(stEventDescription->lable,0,sizeof(stEventDescription->lable));
    memcpy(stEventDescription->lable,m_lable,strlen(m_lable)+1);
}

void setTimeIntervalArr(int *arr,int n)
{
    int i;
    srand(time(NULL));
    for(i=0; i<n; ++i)
    {
        *(arr+i) = rand()%n + 1;
        //*(arr+i) = i+1;
    }
}

int main(int argc, char **argv)
{
    struct event timeout[N];
    struct ST_EventWithDescription stEvent[N];
    int time_interval[N];
    int i=0;

    struct timeval tv;
    struct event_base *base;
    int flags = 0;

    setTimeIntervalArr(time_interval,N);

    base = event_base_new();
    evutil_timerclear(&tv);

    for(i=0; i<N; ++i)
    {
        char buf[BUFLEN]= {0};
        sprintf(buf,"task%d",i+1);
        setParam(stEvent+i,timeout+i,time_interval[i],buf);
        event_assign(timeout+i, base, -1, flags, timeout_cb, (void*)(stEvent+i));
        event_add(timeout+i, &tv);
    }
    
    evutil_gettimeofday(&lasttime, NULL);
    event_base_dispatch(base);

    return (0);
}
相關文章
相關標籤/搜索