Libev库学习2---简单的IO多路复用服务器

本文介绍了一种使用libev和csapp库实现的简单多路复用服务器,该服务器能够处理多个客户端连接请求,并通过事件驱动的方式进行回显。服务器通过监听事件和连接事件的处理,实现了客户端消息接收与发送的功能。
摘要由CSDN通过智能技术生成

1.用到的库

①libev

使用它的对于select,epoll等等的封装来实现IO多路复用

②csapp

使用的是csapp书中的socket函数,以及包装函数来实现一些socket的函数

2.功能

server关注的事件有两个。

①listenfd有连接请求到来的时候,accept,并且将建立的新的socket连接建立为监听事件,加入loop之中。

②在connfd上面有信息过来的时候,进行echo,每次echo一句话。当客户端那边断开的时候,进行一些清理工作。

3.代码

①客户端代码

#include "csapp.h"
#include "csapp.c"

int main(int argc, char**argv)
{
    int clientfd, port;
    char *host, buf_send[MAXLINE], buf_recv[MAXLINE];
    rio_t rio;

    if (argc != 3)
    {
        fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);
        exit(0);
    }
    host = argv[1];
    port = atoi(argv[2]);

    clientfd = Open_clientfd(host, port);
    Rio_readinitb(&rio, clientfd);

    while(Fgets(buf_send, MAXLINE, stdin) != NULL)
    {
        rio_writen(clientfd, buf_send, 1);
        rio_writen(clientfd, buf_send+1 , strlen(buf_send)-1 );
        
        Rio_readlineb(&rio, buf_recv, MAXLINE);
        Fputs(buf_recv, stdout);
    }
    Close(clientfd);
    printf("我退出,我让座,假洒脱。\n");
    exit(0);
}


②服务器代码

#include <ev.h>
#include "csapp.h"
#include "csapp.c"


#define MAXCONN 2

int conn_cnt = 0;
static void listenfd_cb(struct ev_loop *loop, ev_io *w, int revents);
static void connfd_cb(struct ev_loop *loop, ev_io *w, int revents);
ev_io listenfd_watcher;
ev_io connfd_watcher[MAXCONN];

static void
listenfd_cb(struct ev_loop *loop, ev_io *w, int revents)
{
    conn_cnt++;
    if(conn_cnt > MAXCONN)
        app_error("listenfd_cb error: too many clients.");
      //accept for the w->fd, int connfd = accept()

        socklen_t clientlen = sizeof(struct sockaddr_in);
        struct sockaddr_in clientaddr;
        int connfd = Accept(w->fd, (SA *)&clientaddr, &clientlen);

      //find the watcher position for new connfd      

       int i;
       for(i = 0; i < MAXCONN; i++)
       {
               if(connfd_watcher[i].fd == -1)
                    break;
        }

//bind connfd with a new watcher       

        ev_init(&connfd_watcher[i], connfd_cb);
        ev_io_set(&connfd_watcher[i], connfd, EV_READ);
        ev_io_start(loop, &connfd_watcher[i]);

}

static void
connfd_cb(struct ev_loop *loop, ev_io *w, int revents)
{
        //read one line from w->fd
        //then echo it
        
        int bytes_cnt;
        char buf[MAXLINE];
        rio_t rio;
        
        Rio_readinitb(&rio, w->fd);
        if ((bytes_cnt = Rio_readlineb(&rio, buf, MAXLINE)) != 0)
        {
        printf("Server received %d bytes on fd %d\n",
               bytes_cnt, w->fd);
        Rio_writen(w->fd, buf, bytes_cnt);
        }
       else                                //if EOF from client, the do some cleaning.
       {
               conn_cnt--;
               Close(w->fd);
               ev_io_stop(loop, w);
               w->fd = -1;
       }

    
}

int
main(int argc, char **argv)
{
    int listenfd, port;
    socklen_t clientlen = sizeof(struct sockaddr_in);
    struct sockaddr_in clientaddr;
        

    if (argc != 2) {
       fprintf(stderr, "usage: %s <port>\n", argv[0]);
       exit(0);
    }
   
   port = atoi(argv[1]);
   listenfd = Open_listenfd(port);
   int i;
   for(i = 0; i < MAXCONN; i++)
   {
           connfd_watcher[i].fd = -1;
   }

   struct ev_loop *loop = ev_default_loop(0);

   ev_init(&listenfd_watcher, listenfd_cb);
   ev_io_set(&listenfd_watcher, listenfd, EV_READ );
   ev_io_start(loop, &listenfd_watcher);

   ev_run(loop, 0);

   return 0;
   

}

4.注意事项

①编译

由于要采用csapp的一些函数,所以有最开始的两行文件包含的宏命令,csapp.h, csapp.c在网上很容易找到。

在编译的时候也要注意,我的这个服务器文件保存为select.c,所以编译命令为

$gcc select.c -o select -lev -pthread

②解释

服务端程序主要分为四部分

a。宏命令与全局变量

MAXCONN用来表示最多接受多少个连接

conn_cnt用来表示目前已经接受了多少个连接,特别注意:这里没有考虑并发的问题,如果客户端过多,同时退出,每个对conn_cnt减一,可能出错。

然后是两个回调函数前向声明。

然后就是watcher,由于连接请求每次处理一个,所以声明一个即可。

而建立的连接需要同时关注,所以用一个数组。

b。listenfd_watcher回调函数

首先判断,有没有超过最大连接数,有则弹出信息并终止,否则继续

然后接受连接请求、

接着从connfd_watcher数组中,挑出还没有被使用的,判断的标准是,看其fd是否是-1,(我会在main中,先将其置为-1,一旦使用就不是-1)

然后进行一些列的init,set,start

c。connfd_watcher回调函数

这个回调函数的任务就是接受客户端的信息并echo。

所以先定义用于接受信息的变量,然后判断,如果还有信息,则echo一句,并重新等待信息到来。

如果收到EOF,那么进行扫尾工作,

首先,当前连接数减1 然后,关闭fd, 然后将此监听器从loop中注销,然后将监听器的fd置-1,表示此监听器可以被重新使用了。

特别注意:将监听器fd置1,一定要在将监听器从loop中注销之后,因为监听器如果还在loop中其信息是不能更改的。

d。main函数

先进行socket相关操作。

然后将connfd_watcher数组的每个监听器的fd设为1,表示未被使用。

然后就是对于loop和listenfd_watcher的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值