muduo庫使用示例之檔案傳輸filetransfer
阿新 • • 發佈:2018-12-05
檔案傳輸(傳送)有下面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]);
}
}