47.muduo学习笔记之example_下载文件例子

说明

  1. 这个例子源码位于作者的源代码文件夹下examples/filetransfer文件下下

  2. 实现一个发送文件的命令行小工具,这个工具的协议很简单,在启动时通过命令行参数指定要发送的文件,然后在 2021 端口侦听,每当有新连接进来,就把文件内容完整地发送给对方。

  3. 可以直接用telnet测试,只是服务器把文件发过去后,直接在控制台输出

  4. 发送完就调用shutdownWrite()关闭了“写”方向的连接,保留了“读”方向,这称为 TCP half-close,用 shutdown 而不用 close 的效果是,如果对方已经发送了数据,这些数据还“在路上”,那么 muduo 不会漏收这些数据。Muduo 这种关闭连接的方式对对方也有要求,那就是对方 read() 到 0 字节之后会主动关闭连接(无论 shutdownWrite() 还是 close()),一般的网络程序都会这样,不是什么问题。当然,这么做有一个潜在的安全漏洞,万一对方故意不不关,那么muduo 的连接就一直半开着,消耗系统资源。

  5. 连接关闭流程图
    47.muduo学习笔记之example_下载文件例子_第1张图片

  • 那么 muduo 什么时候真正 close socket 呢?在 TcpConnection 对象析构的时候。TcpConnection 持有一个 Socket 对象,Socket 是一个 RAII handler,它的析构函数会 close(sockfd_)

代码,作者一共写了三个版本

第一版

  1. 主要看onConnection()函数,一次性把文件读入内存,一次性调用 send(const string&) 发送完毕,这个版本满足除了“内存消耗只能并发连接数有关,跟文件大小无关”之外的健壮性要求。

  2. 每次建立连接的时候我们都去重新读一遍文件,这是考虑到文件有可能被其他程序修改,如果文件是 immutable 的,整个程序可以共享同一个 fileContent 对象。

  3. 这个版本有一个明显的缺陷,即内存消耗与 (并发连接数 × 文件大小) 成正比,文件越大内存消耗越多,如果文件大小上 GB,只需要建立少量并发连接就能把服务器内存耗尽

第二版

  1. 一块一块地发送文件,减少内存使用,用到了 WriteCompleteCallback,这个版本满足了上述全部健壮性要求。

  2. 为了解决版本一占用内存过多的问题,我们采用流水线的思路,当新建连接时,先发送文件的前 64k 数据,等这块数据发送完毕时再继续发送下 64k 数据,如此往复直到文件内容全部发送完毕。代码中使用了 TcpConnection::setContext() 和getContext() 来保存 TcpConnection 的用户上下文(这里是 FILE*),因此不必使用额外的 std::map 来记住每个连接的当前文件位置。

  3. onWriteComplete()会在每次写完发送后再次调用,直到把数据读完

第三版

  1. 和第二版基本完全一样,但是采用 shared_ptr 来管理 FILE*,避免手动调用::fclose(3)。

你可能感兴趣的:(muduo学习)