前面几节我们讨论了非阻塞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数组。

完整的代码如下:

  1. #include "sysutil.h"
  2. #include "buffer.h"
  3.  
  4. int main(int argc, char const *argv[])
  5. {
  6. //创建client套接字
  7. int sockfd = tcp_client(8934);
  8. //调用非阻塞connect函数
  9. int ret = nonblocking_connect(sockfd, "192.168.44.136", 9981, 5000);
  10. if(ret == -1)
  11. {
  12. fprintf(stderr, "Timeout .\n");
  13. exit(EXIT_FAILURE);
  14. }
  15.  
  16. //将三个fd设置为Non-Blocking
  17. activate_nonblock(sockfd);
  18. activate_nonblock(STDIN_FILENO);
  19. activate_nonblock(STDOUT_FILENO);
  20.  
  21. buffer_t recvbuf; //sockfd -> Buffer -> stdout
  22. buffer_t sendbuf; //stdin -> Buffer -> sockfd
  23.  
  24. //初始化缓冲区
  25. buffer_init(&recvbuf);
  26. buffer_init(&sendbuf);
  27.  
  28. struct pollfd pfd[10];
  29.  
  30. while(1)
  31. {
  32. //初始化
  33. int ix;
  34. for(ix = 0; ix != 3; ++ix)
  35. {
  36. pfd[ix].fd = -1;
  37. pfd[ix].events = 0;
  38. }
  39.  
  40. //重新装填events数组
  41. if(buffer_is_readable(&sendbuf))
  42. {
  43. pfd[0].fd = sockfd;
  44. pfd[0].events |= kWriteEvent;
  45. }
  46. if(buffer_is_writeable(&sendbuf))
  47. {
  48. pfd[1].fd = STDIN_FILENO;
  49. pfd[1].events |= kReadEvent;
  50. }
  51. if(buffer_is_readable(&recvbuf))
  52. {
  53. pfd[2].fd = STDOUT_FILENO;
  54. pfd[2].events |= kWriteEvent;
  55. }
  56. if(buffer_is_writeable(&recvbuf))
  57. {
  58. pfd[0].fd = sockfd;
  59. pfd[0].events |= kReadEvent;
  60. }
  61.  
  62. //监听fd数组
  63. int nready = poll(pfd, 3, 5000);
  64. if(nready == -1)
  65. ERR_EXIT("poll");
  66. else if(nready == 0)
  67. {
  68. printf("timeout\n");
  69. continue;
  70. }
  71. else
  72. {
  73. int i;
  74. for(i = 0; i < 3; ++i)
  75. {
  76. int fd = pfd[i].fd;
  77. if(fd == sockfd && pfd[i].revents & kReadEvent)
  78. {
  79. //从sockfd接收数据到recvbuf
  80. if(buffer_read(&recvbuf, fd) == 0)
  81. {
  82. fprintf(stderr, "server close.\n");
  83. exit(EXIT_SUCCESS);
  84. }
  85. }
  86.  
  87. if(fd == sockfd && pfd[i].revents & kWriteEvent)
  88. buffer_write(&sendbuf, fd); //将sendbuf中的数据写入sockfd
  89.  
  90. if(fd == STDIN_FILENO && pfd[i].revents & kReadEvent)
  91. {
  92. //从stdin接收数据写入sendbuf
  93. if(buffer_read(&sendbuf, fd) == 0)
  94. {
  95. fprintf(stderr, "exit.\n");
  96. exit(EXIT_SUCCESS);
  97. }
  98. }
  99.  
  100. if(fd == STDOUT_FILENO && pfd[i].revents & kWriteEvent)
  101. buffer_write(&recvbuf, fd); //将recvbuf中的数据输出至stdout
  102. }
  103. }
  104. }
  105.  
  106. }

从以上的代码可以看出,大部分操作被封装进了buffer的实现中。

 

测试服务器,我暂时使用muduo库编写一个,代码如下:

  1. #include <muduo/net/TcpServer.h>
  2. #include <muduo/net/InetAddress.h>
  3. #include <muduo/net/TcpConnection.h>
  4. #include <muduo/base/Timestamp.h>
  5. #include <muduo/net/EventLoop.h>
  6. #include <muduo/base/Logging.h>
  7. using namespace muduo;
  8. using namespace muduo::net;
  9.  
  10. void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp t)
  11. {
  12. string s(buf->retrieveAllAsString());
  13. LOG_INFO << "recv msg : " << s.size() << " at: " << t.toFormattedString();
  14. conn->send(s);
  15. }
  16.  
  17. int main(int argc, char const *argv[])
  18. {
  19. EventLoop loop;
  20. InetAddress addr("192.168.44.136", 9981);
  21. TcpServer server(&loop, addr, "EchoServer");
  22. server.setMessageCallback(&onMessage);
  23. server.start();
  24.  
  25. loop.loop();
  26.  
  27. return 0;
  28. }

读者如果使用上述的代码需要安装muduo网络库。

采用以下命令编译:

  1. 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. tortoise git使用 git版本库的rsa key来进行ssh连接

    接触git以来 ,开始时用了命令行,但是命令行总归不如图形化菜单方便明了,而GIT本身自带的GUI又用的不习惯,以前用过许久的TOTORISE SVN,幸好有TORTOISE GIT,这个版本图形化工 ...

  2. [ZJOI2008]树的统计——树链剖分

    本题是一个树链剖分裸题,由于比较菜,老是RE,后来发现是因为使用了全局变量. /************************************************************ ...

  3. (转)MSI - Enable MSI Logging

    转自: http://www.cnblogs.com/atempcode/archive/2007/04/10/707917.html 安装MSI安装包的时候, 有时会遇到错误, 这时LOG文件就非常 ...

  4. error LNK2019: 无法解析的外部符号 _deflate

    我的环境为: Win764 + VS2005 + zlib1.2.8 zlib1.2.8我使用VS2010来编译. ------------------------------------------ ...

  5. 可视化web日志分析工具Logstalgia

    https://blog.csdn.net/zrools/article/details/47250661

  6. 第十四届华中科技大学程序设计竞赛 B Beautiful Trees Cutting【组合数学/费马小定理求逆元/快速幂】

    链接:https://www.nowcoder.com/acm/contest/106/B 来源:牛客网 题目描述 It's universally acknowledged that there'r ...

  7. 数学【p1412】 经营与开发(秦九韶算法)

    顾z 你没有发现两个字里的blog都不一样嘛 qwq 题目描述-->P1412 经营与开发 分析 虽然看到\(Rank_1\)已经有了解释. 但我认为我能BB的更好 我还是决定来写一篇题解. q ...

  8. Xamarin XAML语言教程构建进度条ProgressBar

    Xamarin XAML语言教程构建进度条ProgressBar Xamarin XAML语言教程构建进度条ProgressBar,ProgressBar被称为进度条,它类似于没有滑块的滑块控件.进度 ...

  9. 基于Bootstrap的表格插件bootstrap-table

    写在前面: 表格在项目中是使用比较多的,bootstrap-table插件也是非常好用,而且表格页面也比较好看.这里也简单的记录下. 下面直接看demo吧,代码中都注释了,有些用法,这里没有用到,需要 ...

  10. 七. 多线程编程6.isAlive()和join()的使用

    如前所述,通常你希望主线程最后结束.在前面的例子中,这点是通过在main()中调用sleep()来实现的,经过足够长时间的延迟以确保所有子线程都先于主线程结束.然而,这不是一个令人满意的解决方法,它也 ...