本文无太多内容,主要是几个前面提到过的注意点:

一是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重新实现非阻塞的回射服务器的更多相关文章

  1. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型.   阻塞IO   过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...

  2. epoll 实现回射服务器

    epoll是I/O复用模型中相对epoll和select更高效的实现对套接字管理的函数. epoll有两种模式 LT 和 ET 二者的差异在于 level-trigger 模式下只要某个 socket ...

  3. Linux下select的用法--实现一个简单的回射服务器程序

    1.先看man手册 SYNOPSIS       /* According to POSIX.1-2001 */       #include <sys/select.h>       / ...

  4. Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端

    前面几节我们讨论了非阻塞IO的基本概念.Buffer的设计以及非阻塞connect的实现,现在我们使用它们来完成客户端的编写. 我们在http://www.cnblogs.com/inevermore ...

  5. linux网络编程 IO多路复用 select epoll

    本文以我的小型聊天室为例,对于服务器端的代码,做了三次改进,我将分别介绍阻塞式IO,select,epoll . 一:阻塞式IO 对于聊天室这种程序,我们最容易想到的是在服务器端accept之后,然后 ...

  6. 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    一.基本概念                                                          我们通俗一点讲: Level_triggered(水平触发):当被监控的 ...

  7. epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    转自:http://www.cnblogs.com/yuuyuu/p/5103744.html 一.基本概念                                               ...

  8. Linux非阻塞IO(四)非阻塞IO中connect的实现

    我们为客户端的编写再做一些工作. 这次我们使用非阻塞IO实现connect函数. int connect(int sockfd, const struct sockaddr *addr, sockle ...

  9. Linux非阻塞IO(三)非阻塞IO中缓冲区Buffer的实现

    本文我们来实现回射服务器的Buffer.   Buffer的实现   上节提到了非阻塞IO必须具备Buffer.再次将Buffer的设计描述一下: 这里必须补充一点,writeIndex指向空闲空间的 ...

随机推荐

  1. 放棋游戏(NOIP模拟赛)(DP)

    没有原题... 囧.. [问题描述] 游戏规则是这样,有n(1<=n<=100)行格子,第一行由n个格子,第二行有n-1个格子,第三行由n-2个格子,……以此类推,第n行有1个格子.要求再 ...

  2. usb驱动---What is the difference between /dev/ttyUSB and /dev/ttyACM【转】

    转自:http://blog.csdn.net/ppp2006/article/details/25654733 https://www.rfc1149.net/blog/2013/03/05/wha ...

  3. 在Laravel中使用Middleware进行身份验证

    新建一个中间件: 方法写在handle中 判断用户是否登录而且是否是管理员,不是的话返回到主页 新建判断是否为管理员的方法 在kernel定义一个中间件,key是admin 注册群组路由:prefix ...

  4. Python的网络编程[0] -> socket[2] -> 利用 socket 建立 TCP/UDP 通信

    Socket 目录 socket 的 TCP/IP 通信基本建立过程 socket 的 UDP 通信基本建立过程 socket 的 UDP 广播式通信基本建立过程 socket 的多线程通信建立过程 ...

  5. python获取小程序手机号并绑定

    最近在做小程序开发,在其中也遇到了很多的坑,获取小程序的手机号并绑定就遇到了一个很傻的坑. 流程介绍 官方流程图 小程序使用方法 需要将 <button> 组件 open-type 的值设 ...

  6. JDBC-登陆功能实现

    1.user.java package songyan.jdbc.entity; import java.util.Date; public class User { private int id; ...

  7. mac下eclipse导入windows项目文件乱码

    右键property->resource没有GBK,手工输入"GBK"项目正常. 一开始用windows被坑了,如果再用windows平台,第一时间将encoding改成UT ...

  8. 在SQL Server中查看对象依赖关系

    原文 在SQL Server中查看对象依赖关系 Viewing object dependencies in SQL Server   Deleting or changing objects may ...

  9. Java继承是复制还是共用?答案共用

    继承是复制还是共用?例如 Dog类继承Animal类,在Dog中的属性修改,Animal中的属性也会一起改变吗?如果用Dog和Animal分别实例化对象,dog和animal,这两者的属性是公用还是各 ...

  10. 死磕 Fragment 的生命周期

    死磕 Fragment 的生命周期 本文原创,转载请注明出处.欢迎关注我的 简书 ,关注我的专题 Android Class 我会长期坚持为大家收录简书上高质量的 Android 相关博文.本篇文章已 ...