2017-10-06 5 views
1

나는 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; 
} 
+0

원격 측에서 데이터를 수신 및/또는 처리했는지 여부를 알아야 할 경우 리모컨에서 알려야합니다. –

답변

0

프로세스를 종료 할 때까지 'boost :: asio :: io_service :: work'를 사용하여 IO 서비스 스레드를 활성 상태로 유지할 수 있습니다. 그렇지 않으면 게시 된 모든 작업이 완료되면 io_service :: run이 반환됩니다.

http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/reference/io_service__work.html

난 당신이 종료하고 모든 전송을 반복해서 스레드를 다시 할 거라고 생각하지 않을 것입니다.

조건 변수를 사용하여 io_service 스레드를 종료 한 다음 작업 객체를 삭제하거나 작업 객체를 단순히 파괴하려는 경우 신호를 보낼 수 있습니다.

서버가 보낸 모든 것을 수신 한 시점을 알 수 있습니다. 프로토콜에서 무엇인가를 디자인하거나 TCP의 보장 된 측면에만 의존 할 수 있습니다. 나는 TCP와 IO-Completion을 일반적으로 읽는 것을 제안한다.

+0

감사합니다. 이해를 바탕으로 데이터가 응용 프로그램 수준에서 올바르게 수신되었는지 확인해야합니다. 어떤 종류의 확인 메커니즘을 구현할 것입니다. – ajora