网络文件传输

本章实现一个发送文件的命令行小工具,启动时通过命令行参数指定发送的文件,启用端口侦听网络连接。每次有新连接进来就把文件内容完整的发送给对方。

这里一共提供三个版本

  1. 一次性把文件读入内存,一次性调用send发送数据。
  2. 一块一块地发送文件,减少内存使用
  3. 设计思路同2,但是使用智能指针share_ptr来管理文件描述符FILE*

代码位置:muduo-master/examples/filetransfer

版本一

void onConnection(const TcpConnectionPtr& conn)
{
  LOG_INFO << "FileServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN");
  if (conn->connected())
  {
    LOG_INFO << "FileServer - Sending file " << g_file
             << " to " << conn->peerAddress().toIpPort();
    conn->setHighWaterMarkCallback(onHighWaterMark, 64*1024);
    string fileContent = readFile(g_file);	// 一次性读入文件的全部数据
    conn->send(fileContent);
    conn->shutdown();
    LOG_INFO << "FileServer - done";
  }
}

int main(int argc, char* argv[])
{
  LOG_INFO << "pid = " << getpid();
  if (argc > 1)
  {
    g_file = argv[1];

    EventLoop loop;
    InetAddress listenAddr(2021);
    TcpServer server(&loop, listenAddr, "FileServer");
    // 接收新的连接
    server.setConnectionCallback(onConnection);
    server.start();
    loop.loop();
  }
  else
  {
    fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]);
  }
}

这个版本有一个明显的缺陷,就是内存消耗。这里可以看见,内存消耗与(并发数目*文件大小)呈正相关。如果文件很大,那么所占用的内存也就会很大,所以就有了版本二。

版本二

const int kBufSize = 64*1024;
void onWriteComplete(const TcpConnectionPtr& conn)
{
  FILE* fp = boost::any_cast<FILE*>(conn->getContext());
  char buf[kBufSize];	
  // 每次读取64kb的数据
  size_t nread = ::fread(buf, 1, sizeof buf, fp);
  if (nread > 0)
  {
    conn->send(buf, static_cast<int>(nread));
  }
  else
  {
    ::fclose(fp);
    fp = NULL;
    conn->setContext(fp);
    conn->shutdown();
    LOG_INFO << "FileServer - done";
  }
}

int main(int argc, char* argv[])
{
  LOG_INFO << "pid = " << getpid();
  if (argc > 1)
  {
    g_file = argv[1];

    EventLoop loop;
    InetAddress listenAddr(2021);
    TcpServer server(&loop, listenAddr, "FileServer");
    server.setConnectionCallback(onConnection);
    server.setWriteCompleteCallback(onWriteComplete);	// send数据回调
    server.start();
    loop.loop();
  }
  else
  {
    fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]);
  }
}

版本二采用了流水线的思路,每次读取64kb的数据量,等该数据发送完之后再发送下一个64kb的数据,直到把文件内容发送完。
这种方式每一种连接都会占用一个文件描述符,同时每个连接都会有socket fd,当连接数足够大时,会占用很多文件描述符,所以我们可以看看版本三。

版本三

const int kBufSize = 64*1024;
const char* g_file = NULL;
typedef boost::shared_ptr<FILE> FilePtr;	// 智能指针

void onWriteComplete(const TcpConnectionPtr& conn)
{
  const FilePtr& fp = boost::any_cast<const FilePtr&>(conn->getContext());
  char buf[kBufSize];
  size_t nread = ::fread(buf, 1, sizeof buf, get_pointer(fp));
  if (nread > 0)
  {
    conn->send(buf, static_cast<int>(nread));
  }
  else
  {
    conn->shutdown();
    LOG_INFO << "FileServer - done";
  }
}

int main(int argc, char* argv[])
{
  LOG_INFO << "pid = " << getpid();
  if (argc > 1)
  {
    g_file = argv[1];

    EventLoop loop;
    InetAddress listenAddr(2021);
    TcpServer server(&loop, listenAddr, "FileServer");
    server.setConnectionCallback(onConnection);
    server.setWriteCompleteCallback(onWriteComplete);
    server.start();
    loop.loop();
  }
  else
  {
    fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]);
  }
}

版本三的思路与版本二一致,但是在文件描述符上采用了一个智能指针,所有连接共享一个文件描述符,每一连接维护各自的偏移量,减轻代码资源管理的负担。

相关推荐

  1. 网络文件传输

    2024-07-21 17:02:03       19 阅读
  2. 【计算机网络】SSH文件传输协议

    2024-07-21 17:02:03       51 阅读
  3. UDP传输文件和FTP传输文件

    2024-07-21 17:02:03       18 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 17:02:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 17:02:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 17:02:03       45 阅读
  4. Python语言-面向对象

    2024-07-21 17:02:03       55 阅读

热门阅读

  1. vue2获取视频时长

    2024-07-21 17:02:03       19 阅读
  2. mybatis中的useGeneratedKeys和keyProperty

    2024-07-21 17:02:03       19 阅读
  3. AI Agent的创新之路:AutoGen与LangGraph的比较

    2024-07-21 17:02:03       14 阅读
  4. Android笔试面试题AI答之Activity(3)

    2024-07-21 17:02:03       15 阅读
  5. 关闭终端后继续执行celery任务

    2024-07-21 17:02:03       16 阅读
  6. 学习C语言之 深入了解数据存储

    2024-07-21 17:02:03       16 阅读
  7. WordPress杂技

    2024-07-21 17:02:03       19 阅读
  8. 赞扬的10条原则

    2024-07-21 17:02:03       19 阅读
  9. WHAT - 贪心场景和算法实现

    2024-07-21 17:02:03       19 阅读
  10. 多级联动jquery-cxselect-js的使用

    2024-07-21 17:02:03       19 阅读