libevent学习之网络通信

上一篇博客虽然也写了服务器端的程序,跟着别人的代码写的,没怎么搞懂,之后又看了几篇博客,把别人的代码跑了跑感受了一下,然后自己重新写了一遍,实现了客户端与服务器的通信。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\n",port);

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


        listenfd=socket_init(NULL,port);

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

        /*创建一个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!\n",clifd);
                close(clifd);
        }

        evutil_make_socket_nonblocking(clifd);//设置为非阻塞模式
        printf("accept client %d successfully!\n",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!\n");
                event_free(ev);
                close(fd);
                return ;
        }

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

        char reply_buf[1024] = "I have received the msg: ";
        strcat(reply_buf + strlen(reply_buf), buf);
        write(fd,reply_buf,sizeof(reply_buf));
}

客户端

客户端的实现与服务器端基本相似,先实现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!\n");
                return -1;
        }
        printf("connect to server successfully!\n");
        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 \n");
        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!\n",fd);
                exit(1);
        }

        printf("read %d data from server:%s\n",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!\n");
                exit(1);
        }
		//把终端的消息发送给服务器端 
        int sockfd = *((int*)arg);
         write(sockfd,buf,rv);
}

下面是运行结果:
客户端:
libevent学习之网络通信_第1张图片
服务器端:

你可能感兴趣的:(项目)