您的位置:首页 > 其它

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

2016-02-23 16:49 573 查看
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的操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: