2016-05-16 2 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_client через handle_connect, что совершенно бесполезно и статично. Что я делаю не так?

спасибо, что заранее.


EDIT: Класс сообщения просто класс очень похож на 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_user от handle_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"); 
} 
+0

Я не знаю, что вы подразумеваете под «изнутри класса». Весь этот код выполняется внутри класса. – EJP

+0

@EJP Я добавил некоторую информацию, но, возможно, мне просто становится все труднее понять. – MikelAlejoBR

ответ

1

Вы отправляете данные через соединение, которое уже было закрыто от сверстников.

Непонятно, почему вы хотите аутентифицировать клиента в любое другое время, чем сразу после установления соединения.

+0

Спасибо за ответ. То, что вы говорите, правда, однако представьте себе, что перед этим у вас есть графический интерфейс, и вы хотите вызвать по какой-либо причине «ident_client» или какую-то другую функцию. Видя, что происходит, когда я пытаюсь вызвать «ident_client», я думаю, что столкнулся бы с той же проблемой с любой другой функцией. Итак, можно ли запускать определенные функции вне класса? – MikelAlejoBR

+0

Конечно, это возможно, но если партнер отключился, вы получите обрывы трубчатых ошибок. Здесь у вас есть ошибка протокола приложения. Либо одноранговое соединение еще не должно быть отключено, либо вы не должны отправлять, или вы должны создать новое соединение. – EJP

+0

Извините, возможно, я объяснил это неправильно: вы видите, как я прокомментировал строку «Client :: read_header (...)» внутри функции handle_connect (...)? Если я раскомментирую эту строку и прокомментирую следующий один -Client :: Identify_user (...) - в тот момент, когда я получаю ошибку в канале, если я попытаюсь вызвать функцию из основного. Кстати, я даю ваш ответ как действительный, поскольку по сути вы ответили на мои вопросы. Я просмотрю код. Большое вам спасибо за ваше время. EDIT: Возможно, это потому, что я оставляю пустую часть «if (! Error)», и поэтому io_service заканчивается и останавливается. – MikelAlejoBR