• libevent学习之网络通信


    服务器端
    要实现网络通信,肯定会用到socket等函数,这几个函数应该没什么问题。libevent默认情况下是单线程的,可以配置成多线程,每个线程有一个event_base,对应一个struct event _base结构体以及一个事件管理器,调度托管给它的一系列事件。所以当一个事件发生后,先创建一个event_base,再创建一个事件,将这个事件绑定,然后添加到event_base中,启动event_base的循环,开始处理事件。大概流程如下:

    1 参数解析;
    2. 创建socket连接;
    3. struct event_base *base = event_base_new();创建一个event_base;
    event_base_new()函数分配并且返回一个新的具有默认设置的 event_base。函数会检测环境变量,返回一个到 event_base 的指针。如果发生错误,则返回 NULL。选择各种方法时,函数会选择 OS 支持的最快方法。
    4struct event* listen_event;创建一个监听事件;
    5event_new(base,listenfd,EV_READ | EV_PERSIST,accept_cb,base);使用event_new()函数将监听事件绑定;
    参数:event_base监听的对象,需要监听的事件,事件发生后的回调函数,传给回调函数的参数。libevent支持的事件及属性包括(使用bitfield实现)
    EV_TIMEOUT:超时;
    EV_READ:只要网络缓冲中还有数据,回调函数就会被触发;
    EV_WRITE:只要塞给网络缓冲的数据被写完,回调函数就会被触发;
    EV_SIGNAL:POSIX信号量;
    EV_PERSIST:不指定这个属性,回调函数被触发后事件会被删除;
    EV_ET:Edge-Trigger边缘触发
    6event_add(listen_event,NULL);将监听事件绑定到event_base中;
    7event_base_dispatch(base);启动循环开始处理事件;
    8事件发生时的回调函数typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)
    传给callback_func的是一个监听的事件类型fd,以及event_new中最后一个参数。
    如下是代码:

    int main(int argc,char **argv)
    {
    int listenfd;
    int ch;
    int port;

    struct option opt[]={
    {"port",required_argument,NULL,'p'},
    {"help",no_argument,NULL,'h'},
    {NULL,0,NULL,0}
    };

    while( (ch=getopt_long(argc,argv,"p:h",opt,NULL))!=-1 )
    {
    switch(ch)
    {
    case 'p':
    port=atoi(optarg);
    break;
    case 'h':
    print_help(argv[0]);
    return 0;
    }
    }
    printf("port:%d ",port);

    if( !port )
    {
    print_help(argv[0]);
    return 0;
    }


    listenfd=socket_init(NULL,port);

    if( listenfd<0 )
    {
    printf("socket_init failure! ");
    return -1;
    }
    printf("socket_init successfully! ");

    /*创建一个event_base*/
    struct event_base *base = event_base_new();
    assert(base != NULL);//设置event_base不为空

    /*创建并绑定一个event*/
    struct event* listen_event;

    listen_event=event_new(base,listenfd,EV_READ | EV_PERSIST,accept_cb,base);

    /*添加监听事件*/
    event_add(listen_event,NULL);

    /*启动循环,开始处理事件*/
    event_base_dispatch(base);

    return 0;
    }

    /*回调函数accept_cb*/
    void accept_cb(int fd, short events, void* arg)
    {
    struct sockaddr_in cliaddr;
    evutil_socket_t clifd;
    socklen_t len=sizeof(cliaddr);

    clifd=accept(fd,(struct sockaddr*)&cliaddr,&len);
    if( clifd<0 )
    {
    printf("accept client %d failure! ",clifd);
    close(clifd);
    }

    evutil_make_socket_nonblocking(clifd);//设置为非阻塞模式
    printf("accept client %d successfully! ",clifd);
    struct event_base* base = (struct event_base*)arg;
    /*动态创建一个event结构体,并将其作为回调参数传递给*/
    struct event* cli_event = event_new(NULL, -1, 0, NULL, NULL);
    event_assign(cli_event,base, clifd, EV_READ | EV_PERSIST,read_cb, (void*)cli_event);

    event_add(cli_event,NULL);
    }

    /*accept的回调函数read_cb*/
    void read_cb(int fd, short events, void* arg)
    {
    int rv;
    char buf[1024];
    struct event* ev = (struct event*)arg;

    rv=read(fd,buf,sizeof(buf));
    if( rv<=0 )
    {
    printf("read message failure! ");
    event_free(ev);
    close(fd);
    return ;
    }

    printf("read message successfully:%s ",buf);

    char reply_buf[1024] = "I have received the msg: ";
    strcat(reply_buf + strlen(reply_buf), buf);
    write(fd,reply_buf,sizeof(reply_buf));
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    客户端
    客户端的实现与服务器端基本相似,先实现socket的连接,然后创建event_base,创建并绑定监听事件,添加监听事件,启动循环,开始处理事件。代码如下:

    int main(int argc, char** argv)
    {
    int ch;
    int sockfd;
    char *server_ip=NULL;
    int server_port=0;

    struct option opt[]={
    {"server_ip",required_argument,NULL,'i'},
    {"server_port",required_argument,NULL,'p'},
    {"help",no_argument,NULL,'h'},
    {NULL,0,NULL,0}
    };


    while( (ch=getopt_long(argc,argv,"i:p:h",opt,NULL))!=-1 )
    {
    switch(ch)
    {
    case 'i':
    server_ip=optarg;
    break;
    case 'p':
    server_port=atoi(optarg);
    break;
    case 'h':
    print_help(argv[0]);
    return 0;
    }
    }

    if(!server_port)
    {
    print_help(argv[0]);
    return 0;
    }

    sockfd=socket_connect(server_ip,server_port);
    if( sockfd<0 )
    {
    printf("connect to server failure! ");
    return -1;
    }
    printf("connect to server successfully! ");
    struct event_base* base = event_base_new();

    struct event *sockevent = event_new(base, sockfd, EV_READ | EV_PERSIST, read_cb, NULL);
    event_add(sockevent, NULL);

    //监听终端输入事件
    struct event* ev_input = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, input_cb, (void*)&sockfd);


    event_add(ev_input, NULL);

    event_base_dispatch(base);
    printf("finished ");
    return 0;
    }
    /*读回调函数*/
    void read_cb(int fd, short events, void *arg)
    {
    char buf[1024];
    int rv;

    rv=read(fd,buf,sizeof(buf));
    if( rv<=0 )
    {
    printf("read data from server %dfailure! ",fd);
    exit(1);
    }

    printf("read %d data from server:%s ",rv,buf);

    }
    /*输入信息回调函数*/
    void input_cb(int fd, short events, void* arg)
    {
    char buf[1024];
    int rv;

    rv=read(fd,buf,sizeof(buf));
    if( rv<=0 )
    {
    printf("read failure! ");
    exit(1);
    }
    //把终端的消息发送给服务器端
    int sockfd = *((int*)arg);
    write(sockfd,buf,rv);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    下面是运行结果:
    客户端:

    服务器端:

    --------------------- 

  • 相关阅读:
    Struts2框架的学习遇到的问题1
    博客开通第100天
    RTK(Real Time Kinematic)实时动态差分定位技术
    HSRP 协议/ VRRP 协议(热备份路由协议)
    PKI 公钥基础设施
    路由器的工作原理
    VLAN基础知识
    Linux系统的 粘滞位、sgid和suid
    Kali Linux三步安装中文输入法(极简)
    ACL 包过滤技术
  • 原文地址:https://www.cnblogs.com/hyhy904/p/10935231.html
Copyright © 2020-2023  润新知