`
javatoyou
  • 浏览: 1080844 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

用epoll实现异步的Echo服务器

 
阅读更多

用epoll实现异步的Echo服务器

epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.

用个硬件中的例子吧,可能不太恰当:epoll相当于I/O中断(有的时候才相应),而select相当于轮询(总要反复查询)。

其实epoll比slect好用很多,主要一下几个用法。

struct epoll_event ; epoll事件体,事件发生时候你可以得到一个它。其中epoll_event.data.fd可以存储关联的句柄,epoll_event.event是监听标志,常用的有EPOLLIN (有数据,可以读)、EPOLLOUT(有数据,可以写)EPOLLET(有事件,通用);

(1)创建epoll句柄

int epFd = epoll_create(EPOLL_SIZE);

(2)加入一个句柄到epoll的监听队列

ev.data.fd = serverFd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev);

上面的fd是你要绑定给事件发生时候使用的fd,到时候只能操作这个,下面是事件类型。

使用epoll_ctl添加到之中,EPOLL_CTL_ADD是epoll控制类型,这里监听的fd和给event的fd一般相同。

(3)等待event返回

int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1);

传入的evs是epoll_event的数组,EVENT_ARR应当是不超过这个数组的长度。返回nfds的是不超过EVENT_ARR的数值,表示本次等待到了几个事件。

(4)遍历事件

注意,这里遍历的事件是肯定已经发生了的,而select中遍历的是每个fd,而fd不一定在FDSET中(即不一定有读事件发生)!这是效率最大的差别所在!

for (int i = 0; i < nfds; i++)

{

//do something

}

(5)其他技巧

对事件是否是serverFd判断,如果是,进行accept并加入epoll监听队列,要设置异步读取。

如果evts[i]&EPOLLIN,表示可读,使用read进行试探,如果>0表示连接没有关闭,否则连接已经关闭(出发事件又读取不到东西,表示socket关闭!)。如果<0出错。如果>0,需要继续读取直到为0,但是注意这里的为0是在第一次read不为0的前提下,毕竟我们设置了异步读取,暂时没有数据可以读就返回0了!而如果第一次返回0,那么就是关闭吧!

注意关闭要移出出epoll并且close(clientFd)

罗嗦了好多,看代码!

#相关代码, [四号程序员] http://www.coder4.com
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
* main.cc
*
* Created on: 2009-11-30
* Author: liheyuan
* Describe: epoll实现阻塞模式服务器(Echo服务器)
*
* Last Date: 2009-11-30
* CopyRight: 2009 @ ICT LiHeyuan
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <errno.h>
#define EPOLL_SIZE 10
#define EVENT_ARR 20
#define BACK_QUEUE 10
#define PORT 18001
#define BUF_SIZE 16
void setnonblocking(int sockFd) {
int opt;
//获取sock原来的flag
opt = fcntl(sockFd, F_GETFL);
if (opt < 0) {
printf("fcntl(F_GETFL) fail.");
exit(-1);
}
//设置新的flag,非阻塞
opt |= O_NONBLOCK;
if (fcntl(sockFd, F_SETFL, opt) < 0) {
printf("fcntl(F_SETFL) fail.");
exit(-1);
}
}
int main() {
int serverFd;
//创建服务器fd
serverFd = socket(AF_INET, SOCK_STREAM, 0);
setnonblocking(serverFd);
//创建epoll,并把serverFd放入监听队列
int epFd = epoll_create(EPOLL_SIZE);
struct epoll_event ev, evs[EVENT_ARR];
ev.data.fd = serverFd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev);
//绑定服务器端口
struct sockaddr_in serverAddr;
socklen_t serverLen = sizeof(struct sockaddr_in);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(PORT);
if (bind(serverFd, (struct sockaddr *) &serverAddr, serverLen)) {
printf("bind() fail./n");
exit(-1);
}
//打开监听
if (listen(serverFd, BACK_QUEUE)) {
printf("Listen fail./n");
exit(-1);
}
//死循环处理
int clientFd;
sockaddr_in clientAddr;
socklen_t clientLen;
char buf[BUF_SIZE];
while (1) {
//等待epoll事件的到来,最多取EVENT_ARR个事件
int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1);
//处理事件
for (int i = 0; i < nfds; i++) {
if (evs[i].data.fd == serverFd && evs[i].data.fd & EPOLLIN) {
//如果是serverFd,表明有新连接连入
if ((clientFd = accept(serverFd,
(struct sockaddr *) &clientAddr, &clientLen)) < 0) {
printf("accept fail./n");
}
printf("Connect from %s:%d/n", inet_ntoa(clientAddr.sin_addr),
htons(clientAddr.sin_port));
setnonblocking(clientFd);
//注册accept()到的连接
ev.data.fd = clientFd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epFd, EPOLL_CTL_ADD, clientFd, &ev);
} else if (evs[i].events & EPOLLIN) {
//如果不是serverFd,则是client的可读
if ((clientFd = evs[i].data.fd) > 0) {
//先进行试探性读取
int len = read(clientFd, buf, BUF_SIZE);
if (len > 0) {
//有数据可以读,Echo写入
do {
if (write(clientFd, buf, len) < 0) {
printf("write() fail./n");
}
len = read(clientFd, buf, BUF_SIZE);
} while (len > 0);
} else if (len == 0) {
//出发了EPOLLIN事件,却没有可以读取的,表示断线
printf("Client closed at %d/n", clientFd);
epoll_ctl(epFd, EPOLL_CTL_DEL, clientFd, &ev);
close(clientFd);
evs[i].data.fd = -1;
break;
} else if (len == EAGAIN) {
continue;
} else {
//client读取出错
printf("read() fail.");
}
}
} else {
printf("other event./n");
}
}
}
return 0;
}
分享到:
评论

相关推荐

    Unix Echo服务器和客户端(C语言)

    此外,为了实现并发处理,服务器可能使用了**多线程**或**异步I/O**模型,如`select()`、`poll()`或`epoll()`等机制来监控多个套接字的活动状态,以便在数据可用时立即处理。 文件名为`TCPEchoServerAndClient`的...

    tcpecho.zip_Echo Echo_echo server_echotcpip

    【TCP/IP Echo 服务器原理与实现】 在计算机网络通信中,Echo 服务器是一种常见的测试工具,它接收客户端发送的数据并原样返回,主要用于检查网络连接的可靠性。"tcpecho.zip" 包含了一个基于 TCP/IP 实现的 Echo ...

    evpp based echo server

    1. **异步事件驱动**:EVPP采用Epoll事件通知机制,通过libevent或libev等事件库,实现非阻塞I/O,提高系统并发能力。 2. **C++11标准**:EVPP利用C++11新特性,如lambda函数、智能指针等,提供更现代的编程体验和更...

    linux并发回音服务器与客户端

    - **异步非阻塞I/O**:使用select()、poll()或epoll()等机制,让单个线程能同时处理多个连接。 7. **测试和截图**:项目中的测试截图通常会展示服务器的启动、客户端的连接以及数据的交互过程。这有助于验证程序的...

    半同步反应堆模型实现Echo_Server

    在"Echo_Server"的实现中,我们通常会创建一个服务器,它能够响应客户端发送的数据并原样返回,即所谓的回显服务。这种服务常用于测试网络连接和协议实现。以下是对这个模型和实现的详细解释: 1. **事件库**:在...

    Learning-Echo:做得好玩。 练习学习无阻塞回声服务器。 一个(不是很)功能的实现。 包括阻塞式多线程实现以及非阻塞式异步回显实现,以便充分了解两者之间的区别

    总的来说,"Learning-Echo"项目是一个很好的学习平台,它涵盖了网络编程的核心概念和技术,有助于提升开发者对服务器实现的理解,尤其是无阻塞和异步处理在高并发场景下的应用。通过实践这个项目,不仅可以巩固C语言...

    C++异步网络IO库,仿java的mina实现

    echoserv echo 服务器,把客户端的数据不更改地返回。 编译,提供两种编译模式,windows的cygwin环境,还有linux环境。不用配置,直接make即可。 先编译core工程,在core工程下直接执行make指令 然后编译tools ...

    chibi.rar_EchoServer_c++ 网页游戏_网页 游戏_网页游_网页游戏

    这通常需要用到多线程或异步I/O模型,如epoll或select/poll等机制。这些技术可以提高服务器的并行处理能力,确保在高并发环境下仍能稳定运行。 此外,为了优化网络通信,EchoServer可能会采用TCP/IP协议栈中的各种...

    使用OTP原理构建一个非阻塞的TCP服务器

    4. **tcp_echo_fsm**: 这是处理客户端连接的有限状态机,它实现了echo服务器功能,即接收到客户端的任何数据后,都会原样返回。这对于演示目的非常合适,但在实际应用中,gen_fsm可以处理更复杂的交互逻辑。 为了...

    cpp-uWebSockets最轻量级的高效的可伸缩的WebSocket服务器实现

    1. 高性能:uWebSockets利用了最新的异步I/O技术,如epoll和kqueue,以及libuv库,确保了高效的事件驱动模型,能够处理成千上万的并发连接。 2. 轻量级:uWebSockets代码量小,没有过多的依赖,便于理解和维护,同时...

    basic-binary-ipc:BASIC-BINARY-IPC系统提供了一个接口,用于使用IPv4或本地流执行进程间通信。 该接口遵循非阻塞模式,该模式允许应用程序同步或异步通信

    基本二进制IPC 基本的二进制IPC系统提供了一个接口,用于... 可以使用以下命令启动echo服务器( load " examples/echo-example.lisp " )(echo-example:run-server 12345 ) 可以使用以下命令执行回显客户端( load " ex

    天津工程师范学院09年计算机网络课程设计程序大全

    - **异步编程**:使用异步I/O模型(如select、poll或epoll)提高端口扫描效率。 - **网络套接字编程**:在C/C++或Java中创建和使用套接字,进行网络通信。 这些项目涵盖了计算机网络编程的基础到高级概念,包括...

    netty的入门经典例子的使用

    Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。这个“netty的入门经典例子的使用”很可能是为了引导初学者了解和掌握 Netty 的基本用法和核心概念。下面将...

    PHP扩展swoole,PHP7及以上版本可用

    1. **异步非阻塞I/O**:Swoole利用epoll(Linux)和kqueue(FreeBSD)等事件驱动模型,实现I/O操作的异步非阻塞,提高了服务器处理高并发请求的能力。 2. **协程(Coroutine)**:Swoole在PHP7中引入了协程支持,...

    Netty基础应用实战.docx

    然后,你可以通过创建一个简单的echo服务器和客户端来理解Netty的基本工作原理。Echo服务器会将接收到的数据原样返回,而客户端则发送数据并接收回显。 Netty 的ChannelHandler 和 ChannelPipeline 是处理网络数据...

    libevent demo

    在"libevent demo"中,我们通常会看到一个基础的echoserver示例,它是使用libevent来实现的。Echoserver是一个简单的服务器,它接收客户端发送的数据并原样返回,常用于测试网络连接和数据传输。 **Libevent核心...

    Python的Tornado框架异步编程入门实例

    在实现异步编程的示例代码中,一个简单的echo server可以使用Tornado框架来创建,该server能够接收客户端的连接和数据,并将数据回送给客户端。在这个过程中,服务器端将监听读事件,当读事件发生时,读取数据,并...

    netty-in-action中文版

    - **写一个echo服务器**:通过实现简单的回显服务器来展示Netty的基本使用方法。 - **写一个echo客户端**:与回显服务器相对应,客户端同样重要,用于测试服务器的功能。 - **编译和运行Echo服务器和客户端**:详细...

    TCPecho.rar_in

    9. **多线程/并发处理**:为了同时处理多个客户端的连接,可以使用多线程或异步I/O模型如select、poll或epoll。 通过分析“TCPecho.c”源码,我们可以深入理解上述技术点的具体实现,以及作者如何利用它们来实现TCP...

    1_echo_server

    3. **多线程/异步I/O**:为了处理多个并发的客户端连接,服务器通常需要使用多线程或多进程,或者利用非阻塞I/O和事件驱动模型(如epoll、kqueue)来实现高效的并发处理。 4. **数据处理**:服务器接收到客户端的...

Global site tag (gtag.js) - Google Analytics