43-使用 select 改进客户端

1. 程序路径

代码托管在 gitos 上,请使用下面的命令获取:

git clone https://git.oschina.net/ivan_allen/unp.git

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/echo/multiplexing_select_client

2. 回忆 select

2.1 函数原型

 /* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

上面这些函数相信你不陌生,后面我们写程序会用到。

2.2 selece 的参数与返回值

(1)参数

  • nfds 表示三个集合中最大描述符的值 + 1
  • 后面三个 fd_set 集合分别表示监听哪种类型的事件,分别表示读事件,写事件和异常事件集合
  • 最后一个参数是超时参数,可以为 NULL,表示永远等待

(2)返回值

  • 小于 0,失败
  • 等于 0,超时时间到
  • 大于 0,发生了 IO 事件的个数

3. 改进客户端

在旧版本(processzombie 以其之前我们写过的程序)中,我们使用的客户端基本上都是这样:

void doClient(int sockfd) {
  // 客户端阻塞在标准输入上,一旦客户端收到了服务器的 FIN,也无能为力
  while(fgets(buf, stdin)) {
    write(sockfd, buf);
    read(sockfd, buf);
    puts(buf);
  }
}

缺点是它无法感知服务器进程发来的 FIN 段,现在改进如下(伪代码):

void doClient(int sockfd) {
  fds.add(STDIN_FILENO);
  fds.add(sockfd);
  maxfd = max(STDIN_FILENO, sockfd);

  while(1) {
    rfds = fds;
    // 只监听了标准输入和套接字 sockfd
    nready = select(maxfd + 1, &rfds, NULL, NULL, NULL);
    if (STDIN_FILENO in rfds) {
      if (fgets(buf, stdin) != NULL) {
        write(sockfd, buf);
      }
      else {
        break;
      }
    }

    if (sockfd in rfds) {
      if (read(sockfd, buf) == 0) {
        puts("peer closed");
        break;
      }
      puts(buf);
    }
  }
}

详细代码请参考 unp/program/echo/multiplexing_select_client/echo.cc.

4. 运行结果

  • 在 flower 机器上启动服务器
flower $ ./echo -s -h flower
  • 在 sun 机器上启动客户端
sun $ ./echo -h flower

随意输入一些字符,服务器正常回射。接下来,将服务器子进程强制 kill 掉,出现图 1 的画面:


这里写图片描述
图1 服务器进程杀死后,客户端立即反应过来


这里写图片描述
图2 四次挥手,优雅断开

5. 总结

  • 掌握在网络编程中使用 select

练习 1:将本程序中的 select 改为 poll.
练习 2:使用 select 改进服务器(将多进程改为单进程)
思考:这个程序有很严重的 bug,想想在哪里?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值