1. 程式人生 > >muduo庫使用示例之檔案傳輸filetransfer

muduo庫使用示例之檔案傳輸filetransfer

檔案傳輸(傳送)有下面3個版本,程式碼實現在muduo\examples\filetransfer
目錄中。

版本1:一次性將檔案讀入記憶體併發送

一次性將檔案讀入記憶體中的string fileContent,一次性呼叫send(const string&)傳送完畢。==> 缺點:消耗記憶體很大
註解1:send函式是非阻塞的,立刻返回。不用擔心資料什麼時候傳送給對等方。這個由網路庫muduo負責到底。
註解2:send是非阻塞的,但是在send之後,直接呼叫shutdown,那麼資料會發送完全麼?

conn->send(fileContent);
//傳送fileContent給對方 conn->shutdown();

答案:這樣做是沒有問題的,muduo庫實現了,直到將資料傳送完之後,才真正的關閉寫端,即真正的呼叫shuwdownWrite()。

#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpServer.h>

#include <stdio.h>
#include <unistd.h>

using namespace muduo;
using
namespace muduo::net; const char* g_file = NULL; // FIXME: use FileUtil::readFile() string readFile(const char* filename) { string content; FILE* fp = ::fopen(filename, "rb"); if (fp) { // inefficient!!! const int kBufSize = 1024*1024; char iobuf[kBufSize]; ::setbuffer(fp, iobuf,
sizeof iobuf); //設定檔案的緩衝區 char buf[kBufSize]; size_t nread = 0; //從檔案讀取資料儲存到buf中 while ( (nread = ::fread(buf, 1, sizeof buf, fp)) > 0) { content.append(buf, nread); //將buf新增到content中 } ::fclose(fp); } return content; } void onHighWaterMark(const TcpConnectionPtr& conn, size_t len) { LOG_INFO << "HighWaterMark " << len; } 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); //一次性讀取g_file檔案中所有的內容,存入fileContent中 conn->send(fileContent); //傳送fileContent給對方 conn->shutdown(); //send之後,直接呼叫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]); } }

版本2:一塊一塊的傳送檔案

一塊一塊的傳送檔案,減少記憶體的使用,用到了writeCompleteCallback

#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpServer.h>

#include <stdio.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

void onHighWaterMark(const TcpConnectionPtr& conn, size_t len)
{
  LOG_INFO << "HighWaterMark " << len;
}

const int kBufSize = 64*1024;
const char* g_file = NULL;

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, kBufSize+1);

    FILE* fp = ::fopen(g_file, "rb"); //開啟檔案,返回指標fp
    if (fp)
    {
	 //將conn與fp繫結在一起,那麼通過conn就能獲取fp指標
	 //通過這種方法,就不需要通過map容器來管理這種關係
      conn->setContext(fp); 
      char buf[kBufSize];
      size_t nread = ::fread(buf, 1, sizeof buf, fp); //讀取一塊的內容,存到buf中
      conn->send(buf, static_cast<int>(nread)); //傳送
    }
    else
    {
      conn->shutdown();
      LOG_INFO << "FileServer - no such file";
    }
  }
  else //如果連線失敗
  {
    if (!conn->getContext().empty()) 
    {
      FILE* fp = boost::any_cast<FILE*>(conn->getContext());
      if (fp)
      {
        ::fclose(fp);
      }
    }
  }
}

//傳送完一塊資料,會呼叫onWriteComplete函式
void onWriteComplete(const TcpConnectionPtr& conn)
{
  FILE* fp = boost::any_cast<FILE*>(conn->getContext());
  char buf[kBufSize];
  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);
    server.start();
    loop.loop();
  }
  else
  {
    fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]);
  }
}

版本3:採用shared_ptr管理FILE*

同2,但是採用shared_ptr管理FILE*,此版本不需要手動呼叫::fclose(3)。

//構造std::shared_ptr<FILE>物件管理智慧指標,此處多了一個引數
//表示ctx引用計數減為0是,要銷燬fp這個智慧指標是通過fclos()來銷燬的
   FilePtr ctx(fp, ::fclose); 
#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpServer.h>

#include <stdio.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

void onHighWaterMark(const TcpConnectionPtr& conn, size_t len)
{
  LOG_INFO << "HighWaterMark " << len;
}

const int kBufSize = 64*1024;
const char* g_file = NULL;
typedef std::shared_ptr<FILE> FilePtr; //智慧指標

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, kBufSize+1);

    FILE* fp = ::fopen(g_file, "rb");
    if (fp)
    {
	  //構造std::shared_ptr<FILE>物件管理智慧指標,此處多了一個引數
	  //表示ctx引用計數減為0是,要銷燬fp這個智慧指標是通過fclos()來銷燬的
      FilePtr ctx(fp, ::fclose); 
	  
	  //conn與ctx繫結,那麼通過conn就能獲取ctx指標
      conn->setContext(ctx); 
      char buf[kBufSize];
      size_t nread = ::fread(buf, 1, sizeof buf, fp);
      conn->send(buf, static_cast<int>(nread));
    }
    else
    {
      conn->shutdown();
      LOG_INFO << "FileServer - no such file";
    }
  }
}

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]);
  }
}