zoukankan      html  css  js  c++  java
  • Linux高并发网络编程开发——libevent

    在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

    10-Linux系统编程-第15天(libevent)

    目录:
    一、学习目标
    二、代码分析(epoll_loop.c)
    三、libevent
    1、libevent的安装和测试
    2、解决动态库找不到的问题
    3、event_base讲解
    4、event_base相关的小函数
    5、event事件的创建
    6、消息循环
    7、libevent内部事件的状态转换
    8、使用event读管道
    9、使用event写管道
    10、bufferevent介绍
    11、bufferevent函数介绍
    12、evconnlistener链接监听器
    13、bufferevent实现服务器端代码
    技巧:代码片段(c.snippets)
    四、重要函数总结

    一、学习目标

    二、代码分析

    》分析程序(epoll_loop.c):

      1 /*
      2  * epoll基于非阻塞I/O事件驱动
      3  */
      4 #include <stdio.h>
      5 #include <sys/socket.h>
      6 #include <sys/epoll.h>
      7 #include <arpa/inet.h>
      8 #include <fcntl.h>
      9 #include <unistd.h>
     10 #include <errno.h>
     11 #include <string.h>
     12 #include <stdlib.h>
     13 #include <time.h>
     14 
     15 #define MAX_EVENTS  1024                                    //监听上限数
     16 #define BUFLEN      4096
     17 #define SERV_PORT   8080
     18 
     19 void recvdata(int fd, int events, void *arg);
     20 void senddata(int fd, int events, void *arg);
     21 
     22 /* 描述就绪文件描述符相关信息 */
     23 
     24 struct myevent_s {
     25     int fd;                                                 //要监听的文件描述符
     26     int events;                                             //对应的监听事件
     27     void *arg;                                              //泛型参数
     28     void (*call_back)(int fd, int events, void *arg);       //回调函数
     29     int status;                                             //是否在监听:1->在红黑树上(监听), 0->不在(不监听)
     30     char buf[BUFLEN];
     31     int len;
     32     long last_active;                                       //记录每次加入红黑树 g_efd 的时间值
     33 };
     34 
     35 int g_efd;                                                  //全局变量, 保存epoll_create返回的文件描述符
     36 struct myevent_s g_events[MAX_EVENTS+1];                    //自定义结构体类型数组. +1-->listen fd
     37 
     38 
     39 /*将结构体 myevent_s 成员变量 初始化*/
     40 
     41 void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg)
     42 {
     43     ev->fd = fd;
     44     ev->call_back = call_back;
     45     ev->events = 0;
     46     ev->arg = arg;
     47     ev->status = 0;
     48     //memset(ev->buf, 0, sizeof(ev->buf));
     49     //ev->len = 0;
     50     ev->last_active = time(NULL);    //调用eventset函数的时间
     51 
     52     return;
     53 }
     54 
     55 /* 向 epoll监听的红黑树 添加一个 文件描述符 */
     56 
     57 void eventadd(int efd, int events, struct myevent_s *ev)
     58 {
     59     struct epoll_event epv = {0, {0}};
     60     int op;
     61     epv.data.ptr = ev;
     62     epv.events = ev->events = events;       //EPOLLIN 或 EPOLLOUT
     63 
     64     if (ev->status == 1) {                                          //已经在红黑树 g_efd 里
     65         op = EPOLL_CTL_MOD;                                         //修改其属性
     66     } else {                                //不在红黑树里
     67         op = EPOLL_CTL_ADD;                 //将其加入红黑树 g_efd, 并将status置1
     68         ev->status = 1;
     69     }
     70 
     71     if (epoll_ctl(efd, op, ev->fd, &epv) < 0)                       //实际添加/修改
     72         printf("event add failed [fd=%d], events[%d]
    ", ev->fd, events);
     73     else
     74         printf("event add OK [fd=%d], op=%d, events[%0X]
    ", ev->fd, op, events);
     75 
     76     return ;
     77 }
     78 
     79 /* 从epoll 监听的 红黑树中删除一个 文件描述符*/
     80 
     81 void eventdel(int efd, struct myevent_s *ev)
     82 {
     83     struct epoll_event epv = {0, {0}};
     84 
     85     if (ev->status != 1)                                        //不在红黑树上
     86         return ;
     87 
     88     epv.data.ptr = ev;
     89     ev->status = 0;                                             //修改状态
     90     epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv);                //从红黑树 efd 上将 ev->fd 摘除
     91 
     92     return ;
     93 }
     94 
     95 /*  当有文件描述符就绪, epoll返回, 调用该函数 与客户端建立链接 */
     96 // 回调函数 - 监听的文件描述符发送读事件时被调用
     97 void acceptconn(int lfd, int events, void *arg)
     98 {
     99     struct sockaddr_in cin;
    100     socklen_t len = sizeof(cin);
    101     int cfd, i;
    102 
    103     if ((cfd = accept(lfd, (struct sockaddr *)&cin, &len)) == -1) {
    104         if (errno != EAGAIN && errno != EINTR) {
    105             /* 暂时不做出错处理 */
    106         }
    107         printf("%s: accept, %s
    ", __func__, strerror(errno));
    108         return ;
    109     }
    110 
    111     do {
    112         for (i = 0; i < MAX_EVENTS; i++)                                //从全局数组g_events中找一个空闲元素
    113             if (g_events[i].status == 0)                                //类似于select中找值为-1的元素
    114                 break;                                                  //跳出 for
    115 
    116         if (i == MAX_EVENTS) {
    117             printf("%s: max connect limit[%d]
    ", __func__, MAX_EVENTS);
    118             break;                                                      //跳出do while(0) 不执行后续代码
    119         }
    120 
    121         int flag = 0;
    122         if ((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) {             //将cfd也设置为非阻塞
    123             printf("%s: fcntl nonblocking failed, %s
    ", __func__, strerror(errno));
    124             break;
    125         }
    126 
    127         /* 给cfd设置一个 myevent_s 结构体, 回调函数 设置为 recvdata */
    128 
    129         eventset(&g_events[i], cfd, recvdata, &g_events[i]);   
    130         eventadd(g_efd, EPOLLIN, &g_events[i]);                         //将cfd添加到红黑树g_efd中,监听读事件
    131 
    132     } while(0);
    133 
    134     printf("new connect [%s:%d][time:%ld], pos[%d]
    ", 
    135             inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), g_events[i].last_active, i);
    136     return ;
    137 }
    138 
    139 // 回调函数 - 通信的文件描述符发生读事件时候被调用
    140 void recvdata(int fd, int events, void *arg)
    141 {
    142     struct myevent_s *ev = (struct myevent_s *)arg;
    143     int len;
    144 
    145     len = recv(fd, ev->buf, sizeof(ev->buf), 0);            //读文件描述符, 数据存入myevent_s成员buf中
    146 
    147     eventdel(g_efd, ev);        //将该节点从红黑树上摘除
    148 
    149     if (len > 0) {
    150 
    151         ev->len = len;
    152         ev->buf[len] = '';                                //手动添加字符串结束标记
    153         printf("C[%d]:%s
    ", fd, ev->buf);
    154 
    155         eventset(ev, fd, senddata, ev);                     //设置该 fd 对应的回调函数为 senddata
    156         eventadd(g_efd, EPOLLOUT, ev);                      //将fd加入红黑树g_efd中,监听其写事件
    157 
    158     } else if (len == 0) {
    159         close(ev->fd);
    160         /* ev-g_events 地址相减得到偏移元素位置 */
    161         printf("[fd=%d] pos[%ld], closed
    ", fd, ev-g_events);
    162     } else {
    163         close(ev->fd);
    164         printf("recv[fd=%d] error[%d]:%s
    ", fd, errno, strerror(errno));
    165     }
    166 
    167     return;
    168 }
    169 
    170 // 回调函数 - 通信的文件描述符发生写事件时候被调用
    171 void senddata(int fd, int events, void *arg)
    172 {
    173     struct myevent_s *ev = (struct myevent_s *)arg;
    174     int len;
    175 
    176     len = send(fd, ev->buf, ev->len, 0);                    //直接将数据 回写给客户端。未作处理
    177     /*
    178     printf("fd=%d	ev->buf=%s	tev->len=%d
    ", fd, ev->buf, ev->len);
    179     printf("send len = %d
    ", len);
    180     */
    181 
    182     if (len > 0) {
    183 
    184         printf("send[fd=%d], [%d]%s
    ", fd, len, ev->buf);
    185         eventdel(g_efd, ev);                                //从红黑树g_efd中移除
    186         eventset(ev, fd, recvdata, ev);                     //将该fd的 回调函数改为 recvdata
    187         eventadd(g_efd, EPOLLIN, ev);                       //从新添加到红黑树上, 设为监听读事件
    188 
    189     } else {
    190         close(ev->fd);                                      //关闭链接
    191         eventdel(g_efd, ev);                                //从红黑树g_efd中移除
    192         printf("send[fd=%d] error %s
    ", fd, strerror(errno));
    193     }
    194 
    195     return ;
    196 }
    197 
    198 /*创建 socket, 初始化lfd */
    199 
    200 void initlistensocket(int efd, short port)
    201 {
    202     int lfd = socket(AF_INET, SOCK_STREAM, 0);
    203     fcntl(lfd, F_SETFL, O_NONBLOCK);                                            //将socket设为非阻塞
    204 
    205     /* void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg);  */
    206     eventset(&g_events[MAX_EVENTS], lfd, acceptconn, &g_events[MAX_EVENTS]);
    207 
    208     /* void eventadd(int efd, int events, struct myevent_s *ev) */
    209     eventadd(efd, EPOLLIN, &g_events[MAX_EVENTS]);
    210 
    211     struct sockaddr_in sin;
    212     memset(&sin, 0, sizeof(sin));                                               //bzero(&sin, sizeof(sin))
    213     sin.sin_family = AF_INET;
    214     sin.sin_addr.s_addr = INADDR_ANY;
    215     sin.sin_port = htons(port);
    216 
    217     bind(lfd, (struct sockaddr *)&sin, sizeof(sin));
    218 
    219     listen(lfd, 20);
    220 
    221     return ;
    222 }
    223 
    224 int main(int argc, char *argv[])
    225 {
    226     unsigned short port = SERV_PORT;
    227 
    228     if (argc == 2)
    229         port = atoi(argv[1]);                           //使用用户指定端口.如未指定,用默认端口
    230 
    231     g_efd = epoll_create(MAX_EVENTS+1);                 //创建红黑树,返回给全局 g_efd 
    232     if (g_efd <= 0)
    233         printf("create efd in %s err %s
    ", __func__, strerror(errno));
    234 
    235     initlistensocket(g_efd, port);                      //初始化监听socket
    236 
    237     struct epoll_event events[MAX_EVENTS+1];            //保存已经满足就绪事件的文件描述符数组 
    238     printf("server running:port[%d]
    ", port);
    239 
    240     int checkpos = 0, i;
    241     while (1) {
    242         /* 超时验证,每次测试100个链接,不测试listenfd 当客户端60秒内没有和服务器通信,则关闭此客户端链接 */
    243 
    244         long now = time(NULL);                          //当前时间
    245         for (i = 0; i < 100; i++, checkpos++) {         //一次循环检测100个。 使用checkpos控制检测对象
    246             if (checkpos == MAX_EVENTS)
    247                 checkpos = 0;
    248             if (g_events[checkpos].status != 1)         //不在红黑树 g_efd 上
    249                 continue;
    250 
    251             long duration = now - g_events[checkpos].last_active;       //客户端不活跃的时间
    252 
    253             if (duration >= 60) {
    254                 close(g_events[checkpos].fd);                           //关闭与该客户端链接
    255                 printf("[fd=%d] timeout
    ", g_events[checkpos].fd);
    256                 eventdel(g_efd, &g_events[checkpos]);                   //将该客户端 从红黑树 g_efd移除
    257             }
    258         }
    259 
    260         /*监听红黑树g_efd, 将满足的事件的文件描述符加至events数组中, 1秒没有事件满足, 返回 0*/
    261         int nfd = epoll_wait(g_efd, events, MAX_EVENTS+1, 1000);
    262         if (nfd < 0) {
    263             printf("epoll_wait error, exit
    ");
    264             break;
    265         }
    266 
    267         for (i = 0; i < nfd; i++) {
    268             /*使用自定义结构体myevent_s类型指针, 接收 联合体data的void *ptr成员*/
    269             struct myevent_s *ev = (struct myevent_s *)events[i].data.ptr;  
    270 
    271             if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {           //读就绪事件
    272                 ev->call_back(ev->fd, events[i].events, ev->arg);
    273             }
    274             if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {         //写就绪事件
    275                 ev->call_back(ev->fd, events[i].events, ev->arg);
    276             }
    277         }
    278     }
    279 
    280     /* 退出前释放所有资源 */
    281     return 0;
    282 }

    》分析了

    (1)epoll反应堆模型recvdata函数

    (2)epoll反应堆模型senddata函数

    (3)epoll反应堆模型事件操作流程

    (4)epoll反应堆模型节点事件检测:长时间不工作的结点,删除

    三、libevent

    1、libevent的安装和测试

      

    》练习:源码安装

    libevent库下载:https://libevent.org/

    将 libevent-2.1.8-stable.tar.gz 拷贝至Ubuntu某个目录中(如:Documents/libevent)

    >tar zxvf libevent-2.1.8-stable.tar.gz

    (如果删除包,rm libevent-2.1.8-stable -r)

    >cd libevent-2.1.8-stable

    >ls (查找看是否有configure)

    >./configure

    >ls (查找看是否有makefile)

    >ll Makefile

    >make

    >sudo make install (以root权限拷贝数据到对应的目录)

    (验证是否安装成功)

    >ls (注意:在源码安装目录查看,是否有sample)

    >cd sample

    >ls

    >vi hello-world.c (大概查看下代码,看下端口为9995,实际已经有编译好的,直接执行)

    >./hello-world

    (打开另一个终端,执行>./client 127.0.0.1 9995,查看原终端是否收到flushed answer,即能否通信)

    》如果自己编译的代码,编译时候需要指定库

    如:>gcc hello-world.c -o hello -levent

    为什么是 levent? 在 >cd /usr/local/lib/ ;>ls -l libevent.so ;Linux下动态库的起名是libxxx.so;所以编译时加上 -lxxx

    2、解决动态库找不到的问题

    》问题截图:

    》解决:

    》注意:

    1)第1条中如果源码安装的时候,指定了目录--prefix=/usr/xxxx,需要在指定的目录/usr/xxxx下查找find

    2)第3条练习:(要以管理员权限修改保存)

    3、event_base讲解

    》使用套路:

    》事件处理框架—event_base:

    4、event_base相关的小函数

    》查看event在Linux平台下支持的函数

    >touch event_base_method_support.c

    >vi event_base_method_support.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <event2/event.h>
     8 
     9 int main(int argc, const char* argv[])
    10 {
    11     //创建一个事件处理框架 event_base
    12     struct event_base *base = NULL;
    13     base = event_base_new();
    14     //打印支持后端-IO转接函数
    15     const char** meths = event_get_supported_methods();
    16     for(int i = 0; meths[i] != NULL; ++i)
    17     {
    18         printf("%s
    ", meths[i]);
    19     }
    20     
    21     //查看当前正在使用IO转接函数
    22     printf("current = %s
    ", event_base_get_method(base));
    23     
    24     pid_t pid = fork();
    25     if(pid == 0)
    26     {
    27         //在子进程中重新初始化
    28         event_reinit(base);
    29     }
    30     
    31     //添加事件。。。
    32     
    33     //事件循环
    34     event_base_dispatch(base);
    35     
    36     //释放 event_base
    37     event_base_free(base);
    38     
    39     
    40     return 0;
    41 }

    >gcc event_base_method_support.c -levent

    >./a.out

    (查看输出:epoll,poll,select;——其实这是Linux平台下支持的三个转接函数current = epoll;当前正在使用epoll)

    5、event事件的创建

    》事件的创建(一共两种,这是一种,是不带缓冲区的)--event:

    6、消息循环

    》事件循环:

                

    》练习说明:

    >vi event_base_method_support.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <event2/event.h>
     8 
     9 int main(int argc, const char* argv[])
    10 {
    11     //创建一个事件处理框架 event_base
    12     struct event_base *base = NULL;
    13     base = event_base_new();
    14     //打印支持后端-IO转接函数
    15     const char** meths = event_get_supported_methods();
    16     for(int i = 0; meths[i] != NULL; ++i)
    17     {
    18         printf("%s
    ", meths[i]);
    19     }
    20     
    21     //查看当前正在使用IO转接函数
    22     printf("current = %s
    ", event_base_get_method(base));
    23     
    24     pid_t pid = fork();
    25     if(pid == 0)
    26     {
    27         //在子进程中重新初始化
    28         event_reinit(base);
    29     }
    30     
    31     //添加事件。。。
    32     
    33     //事件循环
    34     //有可能需要很长时间,有可能就是一个死循环
    35     event_base_dispatch(base);
    36     
    37     //释放 event_base
    38     event_base_free(base);
    39     
    40     
    41     return 0;
    42 }

    7、libevent内部事件的状态转换

    》事件的状态转换:

    8、使用event读管道

    >touch read_fifo.c

    >vi read_fifo.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <fcntl.h>
     8 #include <event2/event.h>
     9 
    10 // 对操作处理函数
    11 void read_cb(evutil_socket_t fd, short what, void *arg)
    12 {
    13     // 读管道
    14     char buf[1024] = {0};
    15     int len = read(fd, buf, sizeof(buf));
    16     printf("data len = %d, buf = %s
    ", len, buf);
    17     printf("read event: %s", what & EV_READ ? "Yes" : "No");
    18 }
    19 
    20 
    21 // 读管道
    22 int main(int argc, const char* argv[])
    23 {
    24     unlink("myfifo");//如果存在myfifo,删除文件
    25     //创建有名管道
    26     mkfifo("myfifo", 0664);
    27 
    28     // open file
    29     int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
    30     if(fd == -1)
    31     {
    32         perror("open error");
    33         exit(1);
    34     }
    35 
    36     // 读管道
    37     struct event_base* base = NULL;
    38     base = event_base_new();
    39 
    40     // 创建事件
    41     struct event* ev = NULL;
    42     ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);
    43 
    44     // 添加事件
    45     event_add(ev, NULL);
    46 
    47     // 事件循环
    48     event_base_dispatch(base);
    49 
    50     // 释放资源
    51     event_free(ev);
    52     event_base_free(base);
    53     close(fd);
    54     
    55     return 0;
    56 }

    >gcc read_fifo.c -o read -levent

    9、使用event写管道

    >touch write_fifo.c

    >vi write_fifo.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <fcntl.h>
     8 #include <event2/event.h>
     9 
    10 // 对操作处理函数
    11 void write_cb(evutil_socket_t fd, short what, void *arg)
    12 {
    13     // write管道
    14     char buf[1024] = {0};
    15     static int num = 0;
    16     sprintf(buf, "hello, world == %d
    ", num++);//格式化字符串到buf中
    17     write(fd, buf, strlen(buf)+1);//写入管道
    18 }
    19 
    20 
    21 // 写管道
    22 int main(int argc, const char* argv[])
    23 {
    24     // open file
    25     int fd = open("myfifo", O_WRONLY | O_NONBLOCK);
    26     if(fd == -1)
    27     {
    28         perror("open error");
    29         exit(1);
    30     }
    31 
    32     // 写管道
    33     struct event_base* base = NULL;
    34     base = event_base_new();
    35 
    36     // 创建事件
    37     struct event* ev = NULL;
    38     // 检测的写缓冲区是否有空间写
    39     ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);
    40     
    41     // 添加事件
    42     event_add(ev, NULL);
    43 
    44     // 事件循环
    45     event_base_dispatch(base);
    46 
    47     // 释放资源
    48     event_free(ev);
    49     event_base_free(base);
    50     close(fd);
    51     
    52     return 0;
    53 }

    >gcc write_fifo.c -o write -levent

    >./read

    (打开另一个终端,切换到这个目录下,ls可以看到myfifo管道文件被创建了,然后执行>./write,查看两个终端的执行情况)

    》测试:更改为只处理一次:

    >vi write_fifo.c

    将写改为只写一次:    ev = event_new(base, fd, EV_WRITE, write_cb, NULL);

    >gcc write_fifo.c -o write -levent

    >./read

    (打开另一个终端,切换到这个目录下,ls可以看到myfifo管道文件被创建了,然后执行>./write,查看两个终端的执行情况)

    10、bufferevent介绍

    》带缓冲区的事件一共两种,这是另一种,是带缓冲区的:bufferevent

    11、bufferevent函数介绍

    (1)bufferevent的创建和回调函数的设置(第1、3、4条)

    (2)socket通信客户端连接服务器时用到的函数(第2条)

    (3)bufferevent读写缓冲区是否可用的设置(第5、6条)

            

    》参考手册(libevent参考手册(中文版).pdf)page53-bufferevent的选项标志:

    12、evconnlistener链接监听器

    (1)evconnlistener_new_bind函数(第1条)

    (2)链接监听器对应的小函数(第2、3条)

     

    》参考手册(libevent参考手册(中文版).pdf)page99-100-evconnlistener_new()函数的flag的选项标志:

    13、bufferevent实现服务器端代码

    >mkdir bufferevent

    >cd bufferevent

    >touch server.c

    >vi server.c

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <sys/types.h>
      5 #include <sys/stat.h>
      6 #include <string.h>
      7 #include <event2/event.h>
      8 #include <event2/listener.h>//使用链接监听器evconnlistener_new_bind
      9 #include <event2/bufferevent.h>//用到带缓冲区的事件bufferevent
     10 
     11 // 读缓冲区回调
     12 void read_cb(struct bufferevent *bev, void *arg)
     13 {
     14     char buf[1024] = {0};   
     15     bufferevent_read(bev, buf, sizeof(buf));
     16     printf("recv buf: %s
    ", buf);
     17     
     18     char* p = "我已经收到了你发送的数据!";
     19     //printf("client say: %s
    ", p);
     20 
     21     // 发数据给客户端 - 往缓冲区中写数据
     22     bufferevent_write(bev, p, strlen(p)+1);
     23     //printf("====== send buf: %s
    ", p);
     24     printf("我发送了数据,给客户端...
    ");
     25 }
     26 
     27 // 写缓冲区回调
     28 void write_cb(struct bufferevent *bev, void *arg)
     29 {
     30     //printf("我是写缓冲区的回调函数...
    "); 
     31     printf("发送的数据已经被发送出去了...
    ");
     32 }
     33 
     34 // 事件回调
     35 void event_cb(struct bufferevent *bev, short events, void *arg)
     36 {
     37     if (events & BEV_EVENT_EOF)
     38     {
     39         printf("connection closed
    ");  
     40     }
     41     else if(events & BEV_EVENT_ERROR)   
     42     {
     43         printf("some other error
    ");
     44     }
     45     //释放 bufferevent 资源
     46     bufferevent_free(bev);    
     47     printf("buffevent 资源已经被释放...
    "); 
     48 }
     49 
     50 
     51 //连接完成之后,对应通信操作
     52 void cb_listener(
     53         struct evconnlistener *listener, 
     54         evutil_socket_t fd, 
     55         struct sockaddr *addr, 
     56         int len, void *ptr)
     57 {
     58    printf("connect new client
    ");
     59    //得到传进来的 event_base
     60    struct event_base* base = (struct event_base*)ptr;
     61    //先接收数据 - 发送数据
     62    //将fd 封装为 bufferevent
     63    // 通信操作
     64    // 添加新事件
     65    struct bufferevent *bev = NULL;
     66    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
     67 
     68    // 给bufferevent对应的读写缓冲区设置回调函数
     69    bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
     70    //设置读写缓冲区的回调函数可用,默认写是可用的
     71    bufferevent_enable(bev, EV_READ);
     72 }
     73 
     74 
     75 int main(int argc, const char* argv[])
     76 {
     77 
     78     // init server info
     79     struct sockaddr_in serv;
     80     memset(&serv, 0, sizeof(serv));
     81     serv.sin_family = AF_INET;
     82     serv.sin_port = htons(9876);
     83     serv.sin_addr.s_addr = htonl(INADDR_ANY);//对应0.0.0.0
     84     
     85     //创建监听的事件处理框架
     86     struct event_base* base;
     87     base = event_base_new();
     88     // 创建监听的套接字
     89     // 绑定
     90     // 等待并接收连接请求
     91     struct evconnlistener* listener = NULL;
     92     //有新连接的时候,cb_listener会被调用
     93     listener = evconnlistener_new_bind(base, cb_listener, base, 
     94                                   LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 
     95                                   -1, (struct sockaddr*)&serv, sizeof(serv));
     96     
     97     //开始事件循环
     98     event_base_dispatch(base);
     99     
    100     //释放资源
    101     evconnlistener_free(listener);
    102     event_base_free(base);
    103 
    104     return 0;
    105 }

    >gcc server.c -o server -levent

    >./server

    (打开另一个终端,先把之前的客户端拷贝至bufferevent目录下,然后编译后运行./client 127.0.0.1 9876;然后发送数据,查看原server终端的接收情况)


    》技巧扩展:代码片段

    >cd ~/.vim

    >mkdir UltiSnips

    >touch c.snippets

    >vi c.snippets

      1 #================================= 
      2 #预处理 
      3 #================================= 
      4 #include "..." 
      5 snippet xINC 
      6 #include "${1:TODO}"${2} 
      7 endsnippet 
      8 # #include <...> 
      9 snippet xinc 
     10 #include <${1:TODO}>${2} 
     11 endsnippet 
     12 
     13 
     14 snippet xmyheader
     15 #include <stdio.h>
     16 #include <unistd.h>
     17 #include <stdlib.h>
     18 #include <sys/types.h>
     19 #include <sys/stat.h>
     20 #include <string.h>
     21 ${1}
     22 endsnippet
     23 
     24 snippet xall
     25 #include <stdio.h>
     26 #include <unistd.h>
     27 #include <stdlib.h>
     28 #include <sys/types.h>
     29 #include <sys/stat.h>
     30 #include <string.h>
     31 
     32 int main(int argc, const char* argv[])
     33 {
     34     ${1}
     35     return 0;
     36 }
     37 endsnippet
     38 
     39 snippet xbuf
     40 char ${1}[${2}] = {0};
     41 endsnippet
     42 
     43 #sockaddr_in
     44 snippet xsockaddr_in
     45 struct sockaddr_in ${1}
     46 endsnippet
     47 
     48 # main
     49 snippet xmain
     50 int main(int argc, const char* argv[])
     51 {
     52     ${0}
     53     return 0;
     54 }
     55 endsnippet
     56 
     57 # libevent event callback
     58 snippet xeventcallback
     59 void ${1}(evutil_socket_t fd, short what, void *arg)
     60 {
     61     ${2}
     62 }
     63 ${3}
     64 endsnippet
     65 
     66 # libevent bufferevent listen callback
     67 snippet xlistencallback
     68 void ${1}(
     69         struct evconnlistener *listener, 
     70         evutil_socket_t fd, 
     71         struct sockaddr *addr, 
     72         int len, void *ptr)
     73 {
     74     ${2}
     75 }
     76 ${3}
     77 endsnippet
     78 
     79 # libevent read and write callback
     80 snippet xbread_write_callback
     81 void ${1}(struct bufferevent *bev, void *arg)
     82 {
     83     ${2}
     84 }
     85 ${3}
     86 endsnippet
     87 
     88 # libevent read and write callback
     89 snippet xbevent_callback
     90 void ${1}(struct bufferevent *bev, short events, void *arg)
     91 {
     92     if (events & BEV_EVENT_EOF)
     93     {
     94         printf("connection closed
    ");  
     95     }
     96     else if(events & BEV_EVENT_ERROR)   
     97     {
     98         printf("some other error
    ");
     99     }
    100     ${2}
    101 }
    102 ${3}
    103 endsnippet
    104 
    105 #main(void)
    106 snippet xmainnon
    107 int main(void)
    108 {
    109     ${0}
    110     return 0;
    111 }
    112 endsnippet
    113 
    114 # main args
    115 snippet xargc
    116 if(argc < ${1})
    117 {   
    118     printf("eg: ./a.out ${2}
    ");
    119     exit(1);
    120 }
    121 ${3}
    122 endsnippet
    123 #xxx
    124 snippet xxx
    125 /*
    126  * ${0}
    127  */
    128 endsnippet
    129 
    130 #ErrorPrint
    131 snippet xerror
    132 if(${1}== -1)
    133 {
    134     perror("${2} error");
    135     exit(1);
    136 }
    137 ${3}
    138 endsnippet
    139 
    140 #disconnect
    141 snippet xclose
    142 else if(${1} == 0)
    143 {
    144     printf("客户端已经断开了连接~~~~(>_<)~~~~
    ");
    145     ${2}
    146 }
    147 ${3}
    148 endsnippet
    149 
    150 #ip and port information
    151 snippet xipport
    152 char ip[64] = {0};
    153 printf("New Client IP: %s, Port: %d
    ",
    154     inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)),
    155     ntohs(client_addr.sin_port));
    156 ${1}
    157 endsnippet
    158 #================================= 
    159 #结构语句 
    160 #================================= 
    161 # if 
    162 snippet if 
    163 if (${1:/* condition */}) 
    164 { 
    165     ${2:TODO} 
    166 } 
    167 endsnippet 
    168 # else if 
    169 snippet ei 
    170 else if (${1:/* condition */}) 
    171 { 
    172     ${2:TODO} 
    173 } 
    174 endsnippet 
    175 # else 
    176 snippet el 
    177 else 
    178 { 
    179     ${1:TODO} 
    180 } 
    181 endsnippet 
    182 # return 
    183 snippet re 
    184 return(${1:/* condition */}); 
    185 endsnippet 
    186 # Do While Loop 
    187 snippet do 
    188 do 
    189 { 
    190     ${2:TODO} 
    191 } while (${1:/* condition */}); 
    192 endsnippet 
    193 # While Loop 
    194 snippet wh 
    195 while (${1:/* condition */}) 
    196 { 
    197     ${2:TODO} 
    198 } 
    199 endsnippet 
    200 # switch 
    201 snippet sw 
    202 switch (${1:/* condition */}) 
    203 { 
    204     case ${2:c}: 
    205     { 
    206     } 
    207     break; 
    208 
    209     default: 
    210     { 
    211     } 
    212     break; 
    213 } 
    214 endsnippet 
    215 # 通过迭代器遍历容器(可读写) 
    216 snippet for 
    217 for (auto ${2:iter} = ${1:c}.begin(); ${3:$2} != $1.end(); ${4:++iter}) 
    218 {
    219     ${5:TODO} 
    220 } 
    221 endsnippet 
    222 # 通过迭代器遍历容器(只读) 
    223 snippet cfor 
    224 for (auto ${2:citer} = ${1:c}.cbegin(); ${3:$2} != $1.cend(); ${4:++citer}) 
    225 { 
    226     ${5:TODO} 
    227 } 
    228 endsnippet 
    229 # 通过下标遍历容器 
    230 snippet For 
    231 for (decltype($1.size()) ${2:i} = 0; $2 != ${1}.size(); ${3:++}$2) 
    232 { 
    233     ${4:TODO} 
    234 } 
    235 endsnippet 
    236 # C++11风格for循环遍历(可读写) 
    237 snippet F 
    238 for (auto& e : ${1:c}) 
    239 { 
    240 } 
    241 endsnippet 
    242 # C++11风格for循环遍历(只读) 
    243 snippet CF 
    244 for (const auto& e : ${1:c}) 
    245 { 
    246 } 
    247 endsnippet 
    248 # For Loop 
    249 snippet FOR 
    250 for (unsigned ${2:i} = 0; $2 < ${1:count}; ${3:++}$2) 
    251 { 
    252     ${4:TODO} 
    253 } 
    254 endsnippet 
    255 # try-catch 
    256 snippet try 
    257 try { 
    258 } catch (${1:/* condition */}) { 
    259 } 
    260 endsnippet 
    261 snippet ca 
    262 catch (${1:/* condition */}) { 
    263 } 
    264 endsnippet 
    265 snippet throw 
    266 th (${1:/* condition */}); 
    267 endsnippet 
    268 #================================= 
    269 #容器 
    270 #================================= 
    271 # std::vector 
    272 snippet vec 
    273 vector<${1:char}>   v${2}; 
    274 endsnippet 
    275 # std::list 
    276 snippet lst 
    277 list<${1:char}> l${2}; 
    278 endsnippet 
    279 # std::set 
    280 snippet set 
    281 set<${1:key}>   s${2}; 
    282 endsnippet 
    283 # std::map 
    284 snippet map 
    285 map<${1:key}, ${2:value}>   m${3}; 
    286 endsnippet 
    287 #================================= 
    288 #语言扩展 
    289 #================================= 
    290 # Class 
    291 snippet cl 
    292 class ${1:`Filename('$1_t', 'name')`} 
    293 { 
    294 public: 
    295     $1 (); 
    296     virtual ~$1 (); 
    297 
    298 private: 
    299 }; 
    300 endsnippet 
    301 #================================= 
    302 #结对符 
    303 #================================= 
    304  # 括号 bracket 
    305 snippet b "bracket" i 
    306 (${1})${2} 
    307 endsnippet 
    308 # 方括号 square bracket,设定为 st 而非 sb,避免与 b 冲突
    309 snippet st "square bracket" i 
    310 [${1}]${2} 
    311 endsnippet 
    312 # 大括号 brace 
    313 snippet br "brace" i 
    314 { 
    315         ${1} 
    316 }${2} 
    317 endsnippet 
    318 # 单引号 single quote,设定为 se 而非 sq,避免与 q 冲突
    319 snippet se "single quote" I
    320 '${1}'${2}
    321 endsnippet
    322 # 双引号 quote
    323 snippet q "quote" I
    324 "${1}"${2}
    325 endsnippet
    326 # 指针符号 arrow 
    327 snippet ar "arrow" i 
    328 ->${1} 
    329 endsnippet 
    330 # dot 
    331 snippet d "dot" i 
    332 .${1} 
    333 endsnippet 
    334 # 作用域 scope 
    335 snippet s "scope" i 
    336 ::${1} 
    337 endsnippet
    338 
    339 snippet xtcpcode
    340 #include <stdio.h>
    341 #include <unistd.h>
    342 #include <stdlib.h>
    343 #include <sys/types.h>
    344 #include <string.h>
    345 #include <sys/socket.h>
    346 #include <arpa/inet.h>
    347 #include <ctype.h>
    348 
    349 
    350 int main(int argc, const char* argv[])
    351 {
    352     if(argc < 2)
    353     {
    354         printf("eg: ./a.out port
    ");
    355         exit(1);
    356     }
    357     struct sockaddr_in serv_addr;
    358     socklen_t serv_len = sizeof(serv_addr);
    359     int port = atoi(argv[1]);
    360 
    361     // 创建套接字
    362     int lfd = socket(AF_INET, SOCK_STREAM, 0);
    363     // 初始化服务器 sockaddr_in 
    364     memset(&serv_addr, 0, serv_len);
    365     serv_addr.sin_family = AF_INET;                   // 地址族 
    366     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);    // 监听本机所有的IP
    367     serv_addr.sin_port = htons(port);            // 设置端口 
    368     // 绑定IP和端口
    369     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
    370 
    371     // 设置同时监听的最大个数
    372     listen(lfd, 36);
    373     printf("Start accept ......
    ");
    374 
    375     struct sockaddr_in client_addr;
    376     socklen_t cli_len = sizeof(client_addr);
    377     while(1)
    378     {
    379         ${1}    
    380     }
    381 
    382     close(lfd);
    383     return 0;
    384 }
    385 endsnippet
    386 
    387 snippet xclientcode
    388 #include <stdio.h>
    389 #include <sys/socket.h>
    390 #include <unistd.h>
    391 #include <stdlib.h>
    392 #include <arpa/inet.h>
    393 #include <string.h>
    394 
    395 #define SERV_IP "127.0.0.1"
    396 
    397 int main(int argc, char* argv[])
    398 {
    399     if(argc < 2)
    400     {
    401         printf("./a.out servPort
    ");
    402         exit(1);
    403     }
    404     // 端口
    405     int port = strtol(argv[1], NULL, 10);
    406     int cfd = socket(AF_INET, SOCK_STREAM, 0);
    407 
    408     // 连接服务器
    409     struct sockaddr_in serv_addr;
    410     memset(&serv_addr, 0, sizeof(serv_addr));
    411     serv_addr.sin_family = AF_INET;
    412     inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);
    413     serv_addr.sin_port = htons(port);
    414 
    415     connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    416 
    417     // 接收数据
    418     char buf[BUFSIZ];
    419     int len;
    420     while(1)
    421     {
    422         printf("
    请输入要发送的数据: 
    ");
    423         // 从键盘接受输入
    424         fgets(buf, sizeof(buf), stdin);
    425         // 发送数据给服务器
    426         write(cfd, buf, strlen(buf));
    427 
    428         // 接收服务器数据
    429         len = read(cfd, buf, sizeof(buf));
    430         printf("接收到的数据:
    ");
    431         write(STDOUT_FILENO, buf, len);
    432     }
    433 
    434     close(cfd);
    435     return 0;
    436 }
    437 endsnippet

    四、重要函数总结

      

    在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

  • 相关阅读:
    3D Computer Grapihcs Using OpenGL
    转:认识MyBean
    转:MyBean的安装
    转:MyBean简介
    Delphi常用关键字用法详解
    红鱼儿
    uniGUI-shuiying
    转:RTC搭建android下三层应用程序访问服务器MsSql-客户端
    转:RTC搭建android下三层应用程序访问服务器MsSql-服务器端
    转(Delphi 新窑洞):使用delphi 开发多层应用(十七)使用RTC web 服务器返回JSON
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_15.html
Copyright © 2011-2022 走看看