2016-05-16 57 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