Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端
前面几节我们讨论了非阻塞IO的基本概念、Buffer的设计以及非阻塞connect的实现,现在我们使用它们来完成客户端的编写。
我们在http://www.cnblogs.com/inevermore/p/4049165.html中提出过,客户端需要监听stdin、stdout和sockfd。
这里需要注意的是
只有缓冲区可写的时候,才去监听sockfd和stdin的读事件。
过去在阻塞IO中,我们总是监听sockfd的读事件,因为每当sockfd可读,我们就去调用用户的回调函数处理read事件,在回调函数中需要用户手工read缓冲区的数据。 换句话说,接收数据是用户的责任,poll模型只需要提醒用户去接收即可。
而在非阻塞IO中,因为poll采用的是水平触发,如果缓冲区满了,每次read等于无效操作,那么数据始终堆积在内核中,poll会不停的被触发。这在某种程度上等于轮询。所以我们只在缓冲区可用的情况下监听sockfd的读事件。
只有缓冲区可读的时候,才去监听sockfd和stdout的写事件。因为没有数据可写,监听write事件除了不停的触发poll之外,没有实际意义。
所以每次执行poll之前,需要重新装填poll的events数组。
完整的代码如下:
- #include "sysutil.h"
- #include "buffer.h"
- int main(int argc, char const *argv[])
- {
- //创建client套接字
- int sockfd = tcp_client(8934);
- //调用非阻塞connect函数
- int ret = nonblocking_connect(sockfd, "192.168.44.136", 9981, 5000);
- if(ret == -1)
- {
- fprintf(stderr, "Timeout .\n");
- exit(EXIT_FAILURE);
- }
- //将三个fd设置为Non-Blocking
- activate_nonblock(sockfd);
- activate_nonblock(STDIN_FILENO);
- activate_nonblock(STDOUT_FILENO);
- buffer_t recvbuf; //sockfd -> Buffer -> stdout
- buffer_t sendbuf; //stdin -> Buffer -> sockfd
- //初始化缓冲区
- buffer_init(&recvbuf);
- buffer_init(&sendbuf);
- struct pollfd pfd[10];
- while(1)
- {
- //初始化
- int ix;
- for(ix = 0; ix != 3; ++ix)
- {
- pfd[ix].fd = -1;
- pfd[ix].events = 0;
- }
- //重新装填events数组
- if(buffer_is_readable(&sendbuf))
- {
- pfd[0].fd = sockfd;
- pfd[0].events |= kWriteEvent;
- }
- if(buffer_is_writeable(&sendbuf))
- {
- pfd[1].fd = STDIN_FILENO;
- pfd[1].events |= kReadEvent;
- }
- if(buffer_is_readable(&recvbuf))
- {
- pfd[2].fd = STDOUT_FILENO;
- pfd[2].events |= kWriteEvent;
- }
- if(buffer_is_writeable(&recvbuf))
- {
- pfd[0].fd = sockfd;
- pfd[0].events |= kReadEvent;
- }
- //监听fd数组
- int nready = poll(pfd, 3, 5000);
- if(nready == -1)
- ERR_EXIT("poll");
- else if(nready == 0)
- {
- printf("timeout\n");
- continue;
- }
- else
- {
- int i;
- for(i = 0; i < 3; ++i)
- {
- int fd = pfd[i].fd;
- if(fd == sockfd && pfd[i].revents & kReadEvent)
- {
- //从sockfd接收数据到recvbuf
- if(buffer_read(&recvbuf, fd) == 0)
- {
- fprintf(stderr, "server close.\n");
- exit(EXIT_SUCCESS);
- }
- }
- if(fd == sockfd && pfd[i].revents & kWriteEvent)
- buffer_write(&sendbuf, fd); //将sendbuf中的数据写入sockfd
- if(fd == STDIN_FILENO && pfd[i].revents & kReadEvent)
- {
- //从stdin接收数据写入sendbuf
- if(buffer_read(&sendbuf, fd) == 0)
- {
- fprintf(stderr, "exit.\n");
- exit(EXIT_SUCCESS);
- }
- }
- if(fd == STDOUT_FILENO && pfd[i].revents & kWriteEvent)
- buffer_write(&recvbuf, fd); //将recvbuf中的数据输出至stdout
- }
- }
- }
- }
从以上的代码可以看出,大部分操作被封装进了buffer的实现中。
测试服务器,我暂时使用muduo库编写一个,代码如下:
- #include <muduo/net/TcpServer.h>
- #include <muduo/net/InetAddress.h>
- #include <muduo/net/TcpConnection.h>
- #include <muduo/base/Timestamp.h>
- #include <muduo/net/EventLoop.h>
- #include <muduo/base/Logging.h>
- using namespace muduo;
- using namespace muduo::net;
- void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp t)
- {
- string s(buf->retrieveAllAsString());
- LOG_INFO << "recv msg : " << s.size() << " at: " << t.toFormattedString();
- conn->send(s);
- }
- int main(int argc, char const *argv[])
- {
- EventLoop loop;
- InetAddress addr("192.168.44.136", 9981);
- TcpServer server(&loop, addr, "EchoServer");
- server.setMessageCallback(&onMessage);
- server.start();
- loop.loop();
- return 0;
- }
读者如果使用上述的代码需要安装muduo网络库。
采用以下命令编译:
- g++ server.cpp -lmuduo_net -lmuduo_base -lpthread -o server
下文用poll实现非阻塞的服务器端。
Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端的更多相关文章
- 第二十篇:不为客户连接创建子进程的并发回射服务器(poll实现)
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- 不为客户连接创建子进程的并发回射服务器( poll实现 )
前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...
- Linux下select的用法--实现一个简单的回射服务器程序
1.先看man手册 SYNOPSIS /* According to POSIX.1-2001 */ #include <sys/select.h> / ...
- 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】
下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...
- Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程
1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...
- NIO【同步非阻塞io模型】关于 NIO socket 的详细总结【Java客户端+Java服务端 + 业务层】【可以客户端间发消息】
1.前言 以前使用 websocket来实现双向通信,如今深入了解了 NIO 同步非阻塞io模型 , 优势是 处理效率很高,吞吐量巨大,能很快处理大文件,不仅可以 做 文件io操作, 还可以做sock ...
- Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器
本文无太多内容,主要是几个前面提到过的注意点: 一是epoll的fd需要重新装填.我们将tcp_connection_t的指针保存在数组中,所以我们以这个数组为依据,重新装填fd的监听事件. //重新 ...
- Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO
背景 整理之前学习socket编程的时候复习到了多路复用,搜索了有关资料,了解到多路复用也有局限性,本着打破砂锅问到底的精神,最终找到了关于IO模型的知识点. 在<Unix网络编程>一书中 ...
- Linux非阻塞IO(三)非阻塞IO中缓冲区Buffer的实现
本文我们来实现回射服务器的Buffer. Buffer的实现 上节提到了非阻塞IO必须具备Buffer.再次将Buffer的设计描述一下: 这里必须补充一点,writeIndex指向空闲空间的 ...
随机推荐
- JS 自定义时间格式化
// 对Date的扩展,将 Date 转化为指定格式的String// 月(M).日(d).小时(h).分(m).秒(s).季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占位 ...
- STM in Haskell
Software Transactional Memory,软件事务内存管理(应该是这么翻译的吧T_T) 类似于数据库的事务,所有的操作都有log,最后验证其他线程是否对数据进行修改,要是有那么就回滚 ...
- Charles安装
Charles 是一个网络抓包工具,在做 APP 抓包的时候会用到,相比 Fiddler 来说,Charles 的功能更为强大,而且跨平台支持更好,所以在这里我们选用 Charles 来作为主要的移动 ...
- JAVA快速功能
1.日期格式化 Date date=new Date(); //转换成时间格式12小时制 SimpleDateFormat df_12=new SimpleDateFormat("yyyy- ...
- delphi 开机自动运行代码
unit Unit1;//download by http://www.codefans.netinterface uses Windows,Registry, Messages, SysUtils ...
- 远程服务器的SqlServer允许本地连接
最近做项目都是直接在阿里云买的服务器,并且SqlServer也是安装好的.但是默认的时候,这个服务器上的SqlServer并不允许直接在本地的SqlServer客户端访问,尽管服务器有公网IP. 想要 ...
- 洛谷 P3955 图书管理员【模拟/思维】
题目描述 图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个 正整数. 每位借书的读者手中有一个需求码,这个需求码也是一个正整数.如果一本书的图 书编码恰好以读者的需求码结尾,那 ...
- Django REST framework(官方说明文档翻译)(1快速开始 )
http://www.django-rest-framework.org/tutorial/quickstart/ 第一部分:快速开始 我们将创建一个简单的api接口,用来给admin用户查看及编辑系 ...
- 「kuangbin带你飞」专题二十二 区间DP
layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...
- 「kuangbin带你飞」专题十八 后缀数组
layout: post title: 「kuangbin带你飞」专题十八 后缀数组 author: "luowentaoaa" catalog: true tags: - kua ...