Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器
本文无太多内容,主要是几个前面提到过的注意点:
一是epoll的fd需要重新装填。我们将tcp_connection_t的指针保存在数组中,所以我们以这个数组为依据,重新装填fd的监听事件。
//重新装填epoll内fd的监听事件
int i;
for(i = 0; i < EVENTS_SIZE; ++i)
{
if(connsets[i] != NULL)
{
int fd = i; //fd
tcp_connection_t *pt = connsets[i]; //tcp conn
uint32_t event = 0;
if(buffer_is_readable(&pt->buffer_))
event |= kWriteEvent;
if(buffer_is_writeable(&pt->buffer_))
event |= kReadEvent;
//重置监听事件
epoll_mod_fd(epollfd, fd, event);
}
}
二是,建立连接时,需要做的工作是:
1.新建tcp_connection_t结构,初始化
2.将fd加入epoll,不监听任何事件
3.将tcp_connection_t的指针加入数组。
代码如下:
//建立连接
int peerfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if(peerfd == -1)
ERR_EXIT("accept4");
//新建tcp连接
tcp_connection_t *pt = (tcp_connection_t*)malloc(sizeof(tcp_connection_t));
buffer_init(&pt->buffer_);
//将该tcp连接放入connsets
connsets[peerfd] = pt;
epoll_add_fd(epollfd, peerfd, 0);
连接关闭时需要:
//close
epoll_del_fd(epollfd, fd);
close(fd);
free(pt);
connsets[fd] = NULL;
还有一点:前面我们记录fd和connsets的关系,采用的是数组下标的方式,其实我们还可以将指针存入epoll的data中,其中:
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
我们对于data这个联合体,不再使用fd,而是使用ptr,指向一个tcp_connection_t的指针。不过我们需要将fd存储在tcp_connection_t数据结构中。
这里为了简便起见,仍采用以前的方法,读者可以自行尝试。
完整的代码如下:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sys/socket.h>
#include "sysutil.h"
#include "buffer.h"
#include <assert.h>
#include <sys/epoll.h> #define EVENTS_SIZE 1024 typedef struct{
buffer_t buffer_;
} tcp_connection_t; //表示一条TCP连接 tcp_connection_t *connsets[EVENTS_SIZE]; //提供从fd到TCP连接的映射 int main(int argc, char const *argv[])
{
//获取监听fd
int listenfd = tcp_server("localhost", 9981);
//将监听fd设置为非阻塞
activate_nonblock(listenfd); //初始化connsets
int ix;
for(ix = 0; ix < EVENTS_SIZE; ++ix)
{
connsets[ix] = NULL;
} //初始化epoll
int epollfd = epoll_create1(0);
epoll_add_fd(epollfd, listenfd, kReadEvent);
struct epoll_event events[1024]; while(1)
{
//重新装填epoll内fd的监听事件
int i;
for(i = 0; i < EVENTS_SIZE; ++i)
{
if(connsets[i] != NULL)
{
int fd = i; //fd
tcp_connection_t *pt = connsets[i]; //tcp conn
uint32_t event = 0;
if(buffer_is_readable(&pt->buffer_))
event |= kWriteEvent;
if(buffer_is_writeable(&pt->buffer_))
event |= kReadEvent;
//重置监听事件
epoll_mod_fd(epollfd, fd, event);
}
} //epoll监听fd
int nready = epoll_wait(epollfd, events, 1024, 5000);
if(nready == -1)
ERR_EXIT("epoll wait");
else if(nready == 0)
{
printf("epoll timeout.\n");
continue;
} //处理fd
for(i = 0; i < nready; ++i)
{
int fd = events[i].data.fd;
uint32_t revents = events[i].events;
if(fd == listenfd) //处理listen fd
{
if(revents & kReadREvent)
{
//建立连接
int peerfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if(peerfd == -1)
ERR_EXIT("accept4");
//新建tcp连接
tcp_connection_t *pt = (tcp_connection_t*)malloc(sizeof(tcp_connection_t));
buffer_init(&pt->buffer_);
//将该tcp连接放入connsets
connsets[peerfd] = pt;
epoll_add_fd(epollfd, peerfd, 0);
}
}
else //处理普通客户的fd
{
//取出指针
tcp_connection_t *pt = connsets[fd];
assert(pt != NULL);
if(revents & kReadREvent)
{
if(buffer_read(&pt->buffer_, fd) == 0)
{
//close
epoll_del_fd(epollfd, fd);
close(fd);
free(pt);
connsets[fd] = NULL;
continue; //继续下一次循环
}
} if(revents & kWriteREvent)
{
buffer_write(&pt->buffer_, fd);
}
}
}
} close(listenfd); return 0;
}
下文使用epoll的ET模式。
Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器的更多相关文章
- Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合
上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型. 阻塞IO 过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...
- epoll 实现回射服务器
epoll是I/O复用模型中相对epoll和select更高效的实现对套接字管理的函数. epoll有两种模式 LT 和 ET 二者的差异在于 level-trigger 模式下只要某个 socket ...
- Linux下select的用法--实现一个简单的回射服务器程序
1.先看man手册 SYNOPSIS /* According to POSIX.1-2001 */ #include <sys/select.h> / ...
- Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端
前面几节我们讨论了非阻塞IO的基本概念.Buffer的设计以及非阻塞connect的实现,现在我们使用它们来完成客户端的编写. 我们在http://www.cnblogs.com/inevermore ...
- linux网络编程 IO多路复用 select epoll
本文以我的小型聊天室为例,对于服务器端的代码,做了三次改进,我将分别介绍阻塞式IO,select,epoll . 一:阻塞式IO 对于聊天室这种程序,我们最容易想到的是在服务器端accept之后,然后 ...
- 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
一.基本概念 我们通俗一点讲: Level_triggered(水平触发):当被监控的 ...
- epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
转自:http://www.cnblogs.com/yuuyuu/p/5103744.html 一.基本概念 ...
- Linux非阻塞IO(四)非阻塞IO中connect的实现
我们为客户端的编写再做一些工作. 这次我们使用非阻塞IO实现connect函数. int connect(int sockfd, const struct sockaddr *addr, sockle ...
- Linux非阻塞IO(三)非阻塞IO中缓冲区Buffer的实现
本文我们来实现回射服务器的Buffer. Buffer的实现 上节提到了非阻塞IO必须具备Buffer.再次将Buffer的设计描述一下: 这里必须补充一点,writeIndex指向空闲空间的 ...
随机推荐
- Spring的属性依赖检查
spring支持4种依赖检查:默认的是none none – No dependency checking. simple – If any properties of primitive type ...
- dedeCMS常用功能板块记忆
1.自动调到栏目第一个子栏目 <script> {dede:channel type='son' row='1'} window.location.href='[field:typelin ...
- Linux下的GitHub安装与简单配置教程 ~ 转载
Linux下的GitHub安装与简单配置教程 1.GitHub简介 Git是一个分布式版本控制系统,与其相对的是CVS.SVN等集中式的版本控制系统. 2.Git的安装 1)安装Git a.查看与 ...
- GHC extensions
OverloadedStrings 这是最常见的一个扩展,很多时候都能看到 Haskell中,数字是num的多态,比如: a :: Int a = 1 b :: Double b = 1 ...
- 武汉天喻的NFS 磁盘问题
public void AsyncPaper() { while (true) { try { var jsonText = RedisHelper.BlockPopItemFromList(&quo ...
- POJ 3264.Balanced Lineup-RMQ(ST)详解
先写一道水题的博客,为后面要写的博客做一个铺垫. ヾ(◍°∇°◍)ノ゙ RMQ(Range Minimum/Maximum Query),即区间最值查询,对于长度为n的数列A,回答若干询问RMQ(A, ...
- Codeforces 628 B.New Skateboard
B. New Skateboard time limit per test 1 second memory limit per test 256 megabytes input standar ...
- codeforces-540C
题目连接:http://codeforces.com/problemset/problem/540/C C. Ice Cave time limit per test 2 seconds memory ...
- poj2976(01分数规划)
poj2976 题意 给出 a b 数组,一共 n 对数,其中最多可以去掉 k 对,问怎样使剩下比率(原始比率是 $ \frac{\sum_{i=1}^{n} a}{\sum_{i=1}^{n} b} ...
- 用fiddler测试移动端翻页
大家在移动端是怎么测试翻页的,肯定都是下拉或上滑吧,我也是这样测试的 但如果你要验证数据是否与pc端数据一致时,可能是第一页,第二页看看,或最后几页数据看看,在pc端看简单,直接点击最后一页就行,在移 ...