2016-05-16 56 views
0

我試圖實現一個簡單的客戶端 - 服務器應用程序,其中我將爲客戶端引入一個簡單的API以便與服務器進行交互。問題是,當我嘗試使用該API時,客戶抱怨system:32錯誤,即代表破損管道。但是,如果我從課堂內部調用這個功能,那麼所有的功能都是完美無缺的。Boost Asio:嘗試調用寫入函數時發生故障「externaly」

#include <boost/bind.hpp> 
#include <cstdio> /* sprintf */ 

#include "client.hpp" 
#include "commands.hpp" 

Client::Client(boost::asio::io_service& io_service, 
    boost::asio::ip::tcp::resolver::iterator endpoint_iterator): 
    io_service_(io_service), 
    socket_(io_service) 
{ 
    boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator; 
    socket_.async_connect(endpoint, 
     boost::bind(&Client::handle_connect, this, endpoint_iterator, 
      boost::asio::placeholders::error 
     ) 
    ); 
} 

void Client::handle_connect(boost::asio::ip::tcp::resolver::iterator 
    endpoint_iterator, const boost::system::error_code& error) 
{ 
    if(!error) 
    { 
     //Client::read_header(read_.header(), read_.header_length()); 
     Client::identify_user("User", "Password"); 
    } 
    else if(endpoint_iterator != boost::asio::ip::tcp::resolver::iterator()) 
    { 
     socket_.close(); 
     boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator; 
     socket_.async_connect(endpoint, 
      boost::bind(&Client::handle_connect, this, ++endpoint_iterator, 
       boost::asio::placeholders::error 
      ) 
     ); 
    } 
} 

void Client::handle_readheader(size_t bytes_transferred, 
    const boost::system::error_code& error) 
{ 
    if(bytes_transferred) 
    { 
     if(read_.decode_command() == IDENTIFY) 
     { 
      Client::identify_user("Username", "Password"); 
     } 
    } 
    else 
    { 
     Client::read_header(read_.header(), read_.header_length()); 
    } 
} 

void Client::handle_read(size_t bytes_transferred, 
    const boost::system::error_code& error) 
{ 
    if(bytes_transferred) 
    { 
     Client::read_header(read_.header(), read_.header_length()); 
    } 
    else if(error) 
    { 

    } 
    else 
    { 
     Client::read_header(read_.header(), read_.header_length()); 
    } 
} 

void Client::handle_write(size_t bytes_transferred, 
    const boost::system::error_code& error) 
{ 
    if(bytes_transferred) 
    { 
     Client::read_header(read_.header(), read_.header_length()); 
    } 
    else if(error) 
    { 

    } 
    else 
    { 
     Client::read_header(read_.header(), read_.header_length()); 
    } 
} 


void Client::handle_writeheader(size_t bytes_transferred, 
    const boost::system::error_code& error) 
{ 
    if(bytes_transferred) 
    { 
     if(write_.decode_command() == AUTHENTICATE) 
     { 
      std::cout << "Bodylen:" << write_.decode_size() << std::endl; 
      std::cout << write_.body() << std::endl; 
      Client::write(write_.body(), write_.decode_size()); 
     } 
    } 
    else if(error) 
    { 
     std::cout << "Error HWH: " << error << std::endl; 
    } 
    else 
    { 
     Client::read_header(read_.header(), read_.header_length()); 
    } 
} 

void Client::identify_user(const char* username, const char* password) 
{ 
    size_t tmplen = (strlen(username) + strlen(password) + 2); 
    char tmpbody[tmplen]; 
    std::sprintf(tmpbody, "%s:%s", username, password); 
    write_.body(tmpbody); 
    std::cout << write_.body() << std::endl; 
    write_.encode_header(11, tmplen); 

    std::cout << write_.header() << std::endl; 
    Client::write_header(write_.header(), write_.header_length()); 
} 

void Client::read(char* buffer, size_t len) 
{ 
    boost::asio::async_read(socket_, boost::asio::buffer(buffer, len), 
     boost::bind(&Client::handle_read, this, 
      boost::asio::placeholders::bytes_transferred, 
      boost::asio::placeholders::error 
     ) 
    ); 
} 

void Client::read_header(char* buffer, size_t len) 
{ 
    boost::asio::async_read(socket_, boost::asio::buffer(buffer, len), 
     boost::bind(&Client::handle_readheader, this, 
      boost::asio::placeholders::bytes_transferred, 
      boost::asio::placeholders::error 
     ) 
    ); 
} 

void Client::write_header(char* buffer, size_t len) 
{ 
    boost::asio::async_write(socket_, boost::asio::buffer(buffer, len), 
     boost::bind(&Client::handle_writeheader, this, 
      boost::asio::placeholders::bytes_transferred, 
      boost::asio::placeholders::error 
     ) 
    ); 
} 

void Client::write(char* buffer, size_t len) 
{ 
    boost::asio::async_write(socket_, boost::asio::buffer(buffer, len), 
     boost::bind(&Client::handle_write, this, 
      boost::asio::placeholders::bytes_transferred, 
      boost::asio::placeholders::error 
     ) 
    ); 
} 

int main(){ 
    boost::asio::io_service io_service; 

    boost::asio::ip::tcp::resolver resolver(io_service); 
    boost::asio::ip::tcp::resolver::query query("localhost", "5000"); 
    boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); 

    Client client(io_service, iterator); 

    //client.identify_user("User", "Password"); 

    io_service.run(); 

    return(0); 
} 

在理論上,行爲應該是以下:客戶端發送的認證報頭添加到服務器 - 通過identify_client - ,表示的字節數後面要發送,並等待,直到它接收到ACK - 調用read_header(...) - 。服務器發送確認已準備好處理客戶端發送的ACK。客戶端發送用戶和密碼。

但是,正如我上面所說的,只有在撥打identify_clienthandle_connect時,它纔會起作用,這是完全無用的和靜態的。我究竟做錯了什麼?

非常感謝您提前。


編輯:消息類只是一個類非常相似this one,可以幫助我操縱,輕鬆地解釋協議的消息。


EDIT2:我會盡量解釋它更好一點。我認爲main函數「在課外」是獨立的。那main函數就會創建一個Client對象,它包含從服務器發送和接收數據的API -I附加.hpp文件,希望這可以幫助你更好地理解。

因此,如果我有類似以下的環...

 /* ... */ 

    for(;;) 
    { 
     /* ... */ 

     // Do something here 

     /* If a condition, then authenticate the user */ 
     //client.identify_user("User", "Password"); 

     // Do something there 

     /* ... */ 
    } 

    /* ... */ 

...使用創建的對象,我應該能夠調用該對象的特定成員函數,這將觸發進一步的寫入或讀取。我現在面臨的問題是我無法做到這一點。

如果我從類的handle_connect(...)函數調用identify_user(如上面原始代碼所示),它就起作用。但當然,我無法達到我想要的。

如果我爲了調用main中的函數而對此進行註釋,那麼我會得到損壞的管道錯誤。

if(!error) 
{ 
    //Client::read_header(read_.header(), read_.header_length()); 
    //Client::identify_user("User", "Password"); 
} 

如果我離開這樣的代碼,我會得到相同的管道故障錯誤。

if(!error) 
{ 
    Client::read_header(read_.header(), read_.header_length()); 
    //Client::identify_user("User", "Password"); 
} 

當我瞭解asio時,我看到如果io_service用完了,應用程序就結束了。也許我的問題與此有關。


EDIT4:我喜歡說,我要附加的東西,然後完全忘掉它。

#ifndef CLIENT_HPP 
#define CLIENT_HPP 

#include <boost/asio.hpp> 

#include "message.hpp" 

class Client 
{ 
public: 
    Client(boost::asio::io_service& io_service, 
      boost::asio::ip::tcp::resolver::iterator endpoint_iterator); 

    void identify_user(const char* username, const char* password); 

private: 
    void handle_connect(
     boost::asio::ip::tcp::resolver::iterator endpoint_iterator, 
     const boost::system::error_code& error); 
    void handle_read(size_t bytes_transferred, 
     const boost::system::error_code& error); 
    void handle_readheader(size_t bytes_transferred, 
     const boost::system::error_code& error); 
    void handle_write(size_t bytes_transferred, 
     const boost::system::error_code& error); 
    void handle_writeheader(size_t bytes_transferred, 
     const boost::system::error_code& error); 

    void read(char* buffer, size_t len); 
    void read_header(char* buffer, size_t len); 
    void write_header(char* buffer, size_t len); 
    void write(char* buffer, size_t len); 

    boost::asio::io_service& io_service_; 
    boost::asio::ip::tcp::socket socket_; 

    Message read_; 
    Message write_; 
}; 

#endif /* client.hpp */ 

Edit5:這似乎這樣的伎倆。它確實與io_service有關:

for(;;) 
{ 
    io_service.poll(); 
client.identify_user("User", "Password"); 
} 
+0

我不知道你是什麼意思'從課堂內'。所有這些代碼都是從類內部執行的。 – EJP

+0

@EJP我添加了一些信息,但也許我只是讓它更難理解。 – MikelAlejoBR

回答

1

您正在通過已被對等關閉的連接發送數據。

目前還不清楚爲什麼你想要在建立連接後立即認證客戶端。

+0

感謝您的回答。你所說的是真實的,但想象一下,在這之前你有一個GUI,並且你想由於某種原因「identify_client」或其他一些功能而觸發。看到當我嘗試調用「identify_client」時會發生什麼我想我會遇到與任何其他函數相同的問題。那麼,是否有可能在課堂之外觸發特定功能? – MikelAlejoBR

+0

當然,這是可能的,但如果對方已斷開連接,你會得到破管錯誤。您在這裏遇到的應用程序協議錯誤。或者對方不應該斷開連接,或者你不應該發送,或者你應該創建一個新的連接。 – EJP

+0

對不起,也許我解釋錯了:你看我是如何在「handle_connect(...)」函數裏面評論「Client :: read_header(...)」這一行的?如果我取消註釋該行並註釋以下內容--Client :: Identify_user(...) - 在那個時刻,當我得到管道錯誤時,如果我嘗試從main調用該函數。順便說一句,我給你的答案是有效的答案,基本上你回答了我的問題。我會檢查代碼。非常感謝你的時間。編輯:可能是因爲我留空了「如果(!錯誤)」部分,因此io_service用完了並停止。 – MikelAlejoBR