前面几节我们讨论了非阻塞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实现非阻塞的回射服务器客户端的更多相关文章

  1. 第二十篇:不为客户连接创建子进程的并发回射服务器(poll实现)

    前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...

  2. 不为客户连接创建子进程的并发回射服务器( poll实现 )

    前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...

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

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

  4. 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

    下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...

  5. Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程

    1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...

  6. NIO【同步非阻塞io模型】关于 NIO socket 的详细总结【Java客户端+Java服务端 + 业务层】【可以客户端间发消息】

    1.前言 以前使用 websocket来实现双向通信,如今深入了解了 NIO 同步非阻塞io模型 , 优势是 处理效率很高,吞吐量巨大,能很快处理大文件,不仅可以 做 文件io操作, 还可以做sock ...

  7. Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器

    本文无太多内容,主要是几个前面提到过的注意点: 一是epoll的fd需要重新装填.我们将tcp_connection_t的指针保存在数组中,所以我们以这个数组为依据,重新装填fd的监听事件. //重新 ...

  8. Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO

    背景 整理之前学习socket编程的时候复习到了多路复用,搜索了有关资料,了解到多路复用也有局限性,本着打破砂锅问到底的精神,最终找到了关于IO模型的知识点. 在<Unix网络编程>一书中 ...

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

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

随机推荐

  1. jsp的九大内置对象及EL表达式的隐含对象

    九大内置对象: request         request对象具有请求域,即完成客户端的请求之前,该对象一直有效. response       response对象具有页面作用域,即访问一个页面 ...

  2. UVA 10652 Board Wrapping(二维凸包)

    传送门 刘汝佳<算法竞赛入门经典>P272例题6包装木板 题意:有n块矩形木板,你的任务是用一个面积尽量小的凸多边形把它们抱起来,并计算出木板占整个包装面积的百分比. 输入:t组数据,每组 ...

  3. 设置JPA的Query返回Map对象

    说明正常执行jpa查询的时候需要传一个对应实体进行映射返回的数据,这样有时候如果一个sql是复合sql关联很多表,就需要新建实体有点麻烦,通过下面方式就能将返回结果映射成map.这样就能随意获取返回结 ...

  4. 去掉VS中的警告错误:warning C4819

    当项目引用到外部源代码后,经常出现4819错误,警告信息如下: warning C4819: 该文件包含不能在当前代码页(936)中表示的字符.请将该文件保存为 Unicode 格式以防止数据丢失. ...

  5. springBoot【01】

    /* 使用spring官网的 http://start.spring.io/ 来建立项目包 生成入口文件,入口文件中对类注释@SpringBootApplication,这个注释是唯一的,标明这个类是 ...

  6. 查看 Laravel 的 SQL 语句的方法

    在使用 Laravel 的 Eloquent 进行数据查询的时候,很多小伙伴都想看到背后执行的 SQL 语句到底是什么样的,这小笔录就是解决这个小问题的: 在 Providers/AppService ...

  7. window下Kafka最佳实践

    Kafka的介绍和入门请看这里kafka入门:简介.使用场景.设计原理.主要配置及集群搭建(转) 当前文章从实践的角度为大家规避window下使用的坑. 1.要求: java 6+ 2.下载kafka ...

  8. 牛客练习赛16 F 选值【二分/计数】

    链接:https://www.nowcoder.com/acm/contest/84/F 来源:牛客网 题目描述 给定n个数,从中选出三个数,使得最大的那个减最小的那个的值小于等于d,问有多少种选法. ...

  9. Codeforces Round #445 D. Restoration of string【字符串】

    D. Restoration of string time limit per test 2 seconds memory limit per test 256 megabytes input sta ...

  10. [BZOJ 1509] 逃学的小孩

    Link: BZOJ 1509 传送门 Solution: 一开始受样例影响又犯了想当然的毛病……图中的C点不一定在直径上! 3次$dfs$求出树的直径及直径的两个端点$rt1,rt2$到每个点的距离 ...