我試圖實現一個簡單的客戶端 - 服務器應用程序,其中我將爲客戶端引入一個簡單的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
時,它纔會起作用,這是完全無用的和靜態的。我究竟做錯了什麼?
非常感謝您提前。
編輯:消息類只是一個類非常相似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");
}
我不知道你是什麼意思'從課堂內'。所有這些代碼都是從類內部執行的。 – EJP
@EJP我添加了一些信息,但也許我只是讓它更難理解。 – MikelAlejoBR