88-非阻塞 connect 版本的 web 客户程序

1. web 客户程序

目标:

1) web 客户程序建立与某个 web 服务器的 HTTP 连接,然后获取主页(这只是一个开胃菜,测试我们的 web 程序是否正常工作)。

2) 同时请求多个网络资源,并下载。(看起来很像多线程爬虫,但是我们不使用多线程,而是非阻塞 connect)

我们的 web 客户程序命名为就命名为 web,使用方法如下:

$ ./web <同时允许最大连接数> <主页> <主页文件名> <文件1路径> [文件2路径] ...

例如:

$ ./web 3 www.zcool.com.cn /index.html /img/1.jpg /img/2.jpg

2. 程序设计

  • 请示主页部分
1. 建立连接
2. 发送 GET 请求到服务器:
  GET /index.html HTTP/1.1
  Host: www.zcool.com.cn
  Connection: close // 关闭 Keep-Alive,服务器发送完数据后主动关闭连接。
3. 接收服务器数据,并打印到屏幕。
  • 下载资源部分

设计结构体:

struct file {
  char f_name[256]; // 要下载的资源路径
  char f_host[256]; // 主页
  int f_fd; // 套接字描述符
  int f_flags; // 当前状态,有四种值,分别是 { 0, F_CONNECTING, F_READING, F_DONE }。
};
// 假设我们下载 10 资源
初始化 struct file files[10];

while(1) {
   使用非阻塞i/o, 同时建立多个连接,每一个 f_flags = F_CONNECTING.
   select 监听套接字
   for (f in files) { // 遍历所有文件
     if (f.f_flags == F_CONNECTING) {
       // 检查连接是否成功或失败。使用我们上一篇文章用到的知识,主要是 getsockopt 函数
       如果连接成功,则发起 GET 请求,同时 f_flags = F_READING.
       如果连接失败,f_flags = F_DONE;
     }
     else if (f.f_flags == F_READING) {
       // 下载资源
       nr = read(f.f_fd, buf);
       if (nr == 0) {
         对端关闭, f.f_flags = F_DONE;
       }
     }
   }
}

3. 程序代码

本文使用的程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp

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

main 主函数主要思路是按照第 2 节中的结构来写的,程序主要有这几个函数:

  • homePage:相当于第 2 节中的请求主页部分
  • get:请求资源并打印到屏幕,这个函数只被 homePage 使用
  • startConnect:发起非阻塞 connect,无论成功与否,直接返回
  • request:发起 GET 请求后立即返回,同时 f_flags = F_READING

4. 实验

实验中为了方便启动程序,我所有命令写到了脚本 run.sh 中:


这里写图片描述
图1 run.sh 脚本

run.sh 脚本可以传递一个参数,表示同时最大允许的连接数,默认情况下为 3.

4.1 小试牛刀

先运行一下 run.sh 看看:


这里写图片描述
图2 启动 run.sh,默认最大 3 个连接

可以看到执行完后,耗时为 2.158 秒。

4.2 不同连接数的测试

  • ./run.sh 1


这里写图片描述
图3 同时只允许 1 个连接,时间:5.297s

  • ./run.sh 2


这里写图片描述
图4 同时只允许 2 个连接,时间:2.886s

  • ./run.sh 3


这里写图片描述
图5 同时只允许 3 个连接,时间:2.178s

  • ./run.sh 6


这里写图片描述
图6 同时只允许 6 个连接,时间:1.336s

  • ./run.sh 12


这里写图片描述
图7 同时只允许 12 个连接,时间:1.040s

  • ./run.sh 24


这里写图片描述
图8 同时只允许 24 个连接,时间:0.734s

  • ./run.sh 33

因为我们只同时下载了 33 个文件,所以多余的连接也没什么用。


这里写图片描述
图9 同时只允许 33 个连接,时间:0.890s

这里使用表格来记录一下,下载 33 个文件,使用不同的连接数所消耗的时间:

最大连接数时间(秒)
15.297
22.886
32.178
61.336
121.040
240.734
330.858

从结果可以看到,连接数超过 6 以后,并没有多大提升速度的余地了。连接数从 1 到 2,速度几乎翻倍。

最后,使用非阻塞版本 connect 所付出的代价就是得编写复杂的程序,因此,比较推荐的是使用多线程版本,在 unp 一书中得出的结论是多线程版本的效率并不比非阻塞 connect 差多少,下载速度上大约就几百毫秒的差距,这并不会给客户带来什么影响。

5. 一些可能会踩的坑

客户端一味的追求并发连接数,可能会导致性能下降,特别是在网络状况不好的时候。前面我们介绍过慢启动和拥塞避免算法的细节。

如果同时发起多个连接,其中某个连接遇到 TCP 报文丢失,很可能已经网络已经拥塞,于是执行乘法减小算法(Multiplicative Decrease),但很可惜的是,其它连接并不会得到通知,这种情况下,其它连接仍然还在无脑的一次发送多个 TCP 报文,接下来导致网络更加拥塞。

6. 总结

  • 掌握使用非阻塞 connect 同时发起多个连接的方法
  • 客户端并发连接,可能会遇到性能的问题

练习:使用多线程改写 web 客户端。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值