나는 TCP 클라이언트와 서버를 구현하기 위해 부스트를 사용하고 있습니다. 클라이언트 측에서는 여러 파일을 차례로 보내야합니다. 별도의 메커니즘을 사용하여 서버에 파일 전송을 알립니다. 서버가 파일을 수신 할 준비가되면 클라이언트에 응답하고 전송이 시작됩니다.TCP 파일 전송이 이루어지는 지 확인하는 방법 (C++)
데이터를 쓰고 OS가 io_service.run()
을 호출하여 데이터를 처리하도록 비동기 처리기를 정의했습니다. 내 지식으로는 io_service.run()이 처리 할 핸들러가 더 이상 없을 때까지 차단하지만 데이터가 원격 측에서 실제로 수신되었음을 의미하지는 않습니다. 문제는 io_service.run()
이 반환 된 후 다음 전송을 시작하지만 서버가 첫 번째 수신을 완료하지 못한다는 것입니다.
데이터가 수신되었음을 알리거나 잘못된 것을하고 있음을 알리기 위해 원격 측에서 일종의 외부 메커니즘을 구현해야합니까?
클라이언트 구현 :
#include "StdAfx.h"
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/thread.hpp>
#include "AsyncTCPClient.h"
AsyncTCPClient::AsyncTCPClient(boost::asio::io_service& iIoService, const std::string& iServerIP, const std::string& iPath)
: mResolver(iIoService), mSocket(iIoService)
{
size_t wPos = iServerIP.find(':');
if(wPos==std::string::npos)
{
return;
}
std::string wPortStr = iServerIP.substr(wPos + 1);
std::string wServerIP = iServerIP.substr(0, wPos);
mSourceFile.open(iPath, std::ios_base::binary | std::ios_base::ate);
if(!mSourceFile)
{
LOG(LOGERROR) << "Failed to open file: " << iPath;
return;
}
size_t wFileSize = mSourceFile.tellg();
mSourceFile.seekg(0);
std::ostream wRequestStream(&mRequest);
wRequestStream << iPath << "\n" << wFileSize << "\n\n";
LOG(LOGINFO) << "File to transfer: " << iPath;
LOG(LOGINFO) << "Filesize: " << wFileSize << " bytes";
tcp::resolver::query wQuery(wServerIP, wPortStr);
mResolver.async_resolve(wQuery, boost::bind(&AsyncTCPClient::HandleResolve, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator));
}
AsyncTCPClient::~AsyncTCPClient()
{
}
void AsyncTCPClient::HandleResolve(const boost::system::error_code & iErr, tcp::resolver::iterator iEndpointIterator)
{
if(!iErr)
{
tcp::endpoint wEndpoint = *iEndpointIterator;
mSocket.async_connect(wEndpoint, boost::bind(&AsyncTCPClient::HandleConnect, this, boost::asio::placeholders::error, ++iEndpointIterator));
}
else
{
LOG(LOGERROR) << "Error: " << iErr.message();
}
}
void AsyncTCPClient::HandleConnect(const boost::system::error_code &iErr, tcp::resolver::iterator iEndpointIterator)
{
if(!iErr)
{
boost::asio::async_write(mSocket, mRequest, boost::bind(&AsyncTCPClient::HandleWriteFile, this, boost::asio::placeholders::error));
}
else if(iEndpointIterator != tcp::resolver::iterator())
{
mSocket.close();
tcp::endpoint wEndpoint = *iEndpointIterator;
mSocket.async_connect(wEndpoint, boost::bind(&AsyncTCPClient::HandleConnect, this, boost::asio::placeholders::error, ++iEndpointIterator));
}
else
{
LOG(LOGERROR) << "Error: " << iErr.message();
}
}
void AsyncTCPClient::HandleWriteFile(const boost::system::error_code& iErr)
{
if(!iErr)
{
if(mSourceFile)
{
mSourceFile.read(mBuffer.c_array(), (std::streamsize)mBuffer.size());
// EOF reached
if(mSourceFile.gcount() <= 0)
{
LOG(LOGINFO) << "File transfer done";
return;
}
//LOG(LOGTRACE) << "Send " << mSourceFile.gcount() << "bytes, total: " << mSourceFile.tellg() << " bytes.\n";
boost::asio::async_write(mSocket, boost::asio::buffer(mBuffer.c_array(), mSourceFile.gcount()), boost::bind(&AsyncTCPClient::HandleWriteFile, this, boost::asio::placeholders::error));
}
else
{
LOG(LOGINFO) << "File transfer done";
return;
}
}
else
{
LOG(LOGERROR) << "Error value: " << iErr.value();
LOG(LOGERROR) << "Error message: " << iErr.message();
throw std::exception();
}
}
서버 구현 :
#include "StdAfx.h"
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <fstream>
#include <boost/enable_shared_from_this.hpp>
#include "AsyncTCPClient.h"
#include "AsyncTCPServer.h"
#include "Debug.h"
AsyncTCPServer::AsyncTCPServer(unsigned short iPort, const std::string iFilePath)
:mAcceptor(mIoService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), iPort), true)
{
mAsyncTCPConnectionPtr wNewConnection(new AsyncTCPConnection(mIoService, iFilePath));
mAcceptor.async_accept(wNewConnection->Socket(), boost::bind(&AsyncTCPServer::HandleAccept, this, wNewConnection, boost::asio::placeholders::error));
mIoService.run();
}
AsyncTCPServer::~AsyncTCPServer()
{
mIoService.stop();
}
void AsyncTCPServer::HandleAccept(mAsyncTCPConnectionPtr iCurConnection, const boost::system::error_code& iErr)
{
if (!iErr)
{
iCurConnection->Start();
}
else
{
BIOLOG(BioSans::LOGERROR) << " " << iErr << ", " << iErr.message();
}
}
연결 구현 :
#include "StdAfx.h"
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <fstream>
#include "Debug.h"
#include "AsyncTCPConnection.h"
AsyncTCPConnection::AsyncTCPConnection(boost::asio::io_service& iIoService, const std::string iFilePath)
: mSocket(iIoService), mFileSize(0), mFilePath(iFilePath)
{
}
AsyncTCPConnection::~AsyncTCPConnection()
{
}
void AsyncTCPConnection::Start()
{
LOG(LOGINFO) << "Start";
async_read_until(mSocket, mRequestBuffer, "\n\n", boost::bind(&AsyncTCPConnection::HandleReadRequest, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void AsyncTCPConnection::HandleReadRequest(const boost::system::error_code& iErr, std::size_t iBytesTransferred)
{
if(iErr)
{
return HandleError(__FUNCTION__, iErr);
}
LOG(LOGTRACE) << "(" << iBytesTransferred << ")" << ", in_avail = " << mRequestBuffer.in_avail() << ", size = " << mRequestBuffer.size() << ", max_size = " << mRequestBuffer.max_size();
std::istream wRequestStream(&mRequestBuffer);
std::string wFilePath;
wRequestStream >> wFilePath;
wRequestStream >> mFileSize;
wRequestStream.read(mBuffer.c_array(), 2);
mOutputFile.open(mFilePath, std::ios_base::binary);
if(!mOutputFile)
{
LOG(LOGERROR) << "Failed to open: " << wFilePath;
return;
}
do
{
wRequestStream.read(mBuffer.c_array(), (std::streamsize)mBuffer.size());
LOG(LOGTRACE) << "Write " << wRequestStream.gcount() << " bytes";
mOutputFile.write(mBuffer.c_array(), wRequestStream.gcount());
}
while(wRequestStream.gcount() > 0);
async_read(mSocket, boost::asio::buffer(mBuffer.c_array(), mBuffer.size()),boost::bind(&AsyncTCPConnection::HandleReadFileContent, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void AsyncTCPConnection::HandleReadFileContent(const boost::system::error_code& iErr, std::size_t iBytesTransferred)
{
if(iBytesTransferred>0)
{
mOutputFile.write(mBuffer.c_array(), (std::streamsize)iBytesTransferred);
LOG(LOGTRACE) << "Received " << mOutputFile.tellp() << " bytes";
if (mOutputFile.tellp()>=(std::streamsize)mFileSize)
{
return;
}
}
if(iErr)
{
return HandleError(__FUNCTION__, iErr);
}
async_read(mSocket, boost::asio::buffer(mBuffer.c_array(), mBuffer.size()), boost::bind(&AsyncTCPConnection::HandleReadFileContent, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void AsyncTCPConnection::HandleError(const std::string& function_name, const boost::system::error_code& err)
{
LOG(LOGERROR) << " in " << function_name <<" due to " << err <<" " << err.message();
}
코드 보낼 파일 :
void SendFile(std::string iFilePath, std::string iServerIP)
{
static int wRetries = 0;
try
{
boost::asio::io_service wIoService;
LOG(LOGINFO) << "Sending data to: " << iServerIP;
LOG(LOGINFO) << "Filename is: " << iFilePath;
AsyncTCPClient client(wIoService, iServerIP, iFilePath);
wIoService.run();
// here I want to make sure that the data got to the remote host
// it looks like wIoService.run() returns once bytes are written to the socket
}
catch(std::exception)
{
// retry 3 times in case something goes wrong
if(wRetries < 3)
{
wRetries++;
LOG(LOGWARNING) << "Problem sending file : " << iFilePath << " to address: " << iServerIP;
LOG(LOGWARNING) << "Retry #" << wRetries;
SendFile(iFilePath, iServerIP);
}
else
{
LOG(LOGERROR) << "Unable to send file: " << iFilePath << " to address: " << iServerIP;
wRetries = 0;
return;
}
}
wRetries = 0;
}
원격 측에서 데이터를 수신 및/또는 처리했는지 여부를 알아야 할 경우 리모컨에서 알려야합니다. –