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指向空闲空间的 ...
随机推荐
- Socket学习进阶之基础通信
服务端代码: using System; using System.Text; using System.Net; using System.Net.Sockets; public class ser ...
- [ CodeVS冲杯之路 ] P3117
不充钱,你怎么AC? 题目:http://codevs.cn/problem/3117/ 啊啊啊,基础的高精度乘法被我写得又臭又长,以后再来优化代码(DP着哪天能够把加减乘除全部写一边贴上来,哦对还有 ...
- 培训补坑(day4:网络流建模与二分图匹配)
补坑时间到QAQ 好吧今天讲的是网络流建模与二分图匹配... day3的网络流建模好像说的差不多了.(囧) 那就接着补点吧.. 既然昨天讲了建图思想,那今天就讲讲网络流最重要的技巧:拆点. 拆点,顾名 ...
- (转)Python 操作 Windows 粘贴板
转自: http://outofmemory.cn/code-snippet/3939/Python-operation-Windows-niantie-board Python 操作 Windows ...
- MFC学习之EDIT控件初始化
//四种方法为EDIT控件初始化 //调用系统API HWND hEidt = ::GetDlgItem(m_hWnd,IDC_EDIT1); ::SetWindowText( ...
- myeclipse10.7配置resin4.0.36
Resin-4.0.35 (built Tue, 12 Feb 2013 10:05:50 PST) Copyright(c) 1998-2012 Caucho Technology. All ri ...
- Python_Tips[2] -> 函数延后估值及字节码分析
函数延后估值及字节码分析 在一个循环中定义了函数 f 但是并未对其进行调用,在循环结束后调用,此时i值为3故最终3个函数输出均为9.而非1, 4, 9. 这是由于在定义闭包函数 f 时,传入变量 i, ...
- #420 Div2 D
#420 Div2 D 题意 给出一个方格矩阵,其中存在亮着的方格,只能在亮着的方格上行走,可以在初始亮的方格上花费一枚硬币临时点亮任意一行或一列,地图上同一时间只能存在一个这样的行或列,问走到终点最 ...
- 「Codeforces Round #441」 Classroom Watch
Discription Eighth-grader Vova is on duty today in the class. After classes, he went into the office ...
- Unity3D的主要类图
原文:http://blog.teotigraphix.com/2011/05/17/unity3d-uml-gameobject-cheat-sheet/ 1. GameObject_Structu ...