Boost Asio:尝试调用写入函数时断管“externaly”

时间:2016-05-16 18:13:54

标签: c++ api boost boost-asio handler

我正在尝试实现一个简单的客户端 - 服务器应用程序,我将在其中为客户端引入一个简单的API,以便与服务器进行交互。问题是,当我尝试使用该API时,客户端会抱怨system:32错误,它代表断管。但是,如果我从课堂内调用该函数,一切都会完美无缺。

    #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_client调用handle_connect时,它似乎才有效,这是完全无用且静态的。我做错了什么?

非常感谢你。

编辑:消息类只是一个与this one非常相似的类,它可以帮助我操作并轻松解释协议的消息。

EDIT2:我会尝试更好地解释一下。我认为main函数“在类之外”,作为独立的东西。那个main函数然后创建一个Client对象,其中包含用于从服务器发送和接收数据的API - 我附加了 .hpp 文件,希望这有助于您理解更好 -

因此,如果我有一个类似下面的循环...

        /* ... */

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

        // Do something here

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

        // Do something there

        /* ... */
    }

    /* ... */

...使用该创建的对象我应该能够调用该对象的特定成员函数,这将触发进一步的写入或读取。我现在面临的问题是,我无法做到这一点。

如果我从上面原始代码中显示的identify_userhandle_connect(...)函数中调用Client,则可以正常工作。但当然,我无法实现我的目标。

如果我发表评论以便从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");
}

1 个答案:

答案 0 :(得分:1)

您通过已经被对等方关闭的连接发送数据。

目前尚不清楚为什么要在建立连接后立即在任何其他时间对客户端进行身份验证。