Boost Asio:尝试调用写入函数时管道损坏 "externaly"
Boost Asio: broken pipe when trying to call a write function "externaly"
我正在尝试实现一个简单的客户端-服务器应用程序,其中我将为客户端引入一个简单的 API 以便与服务器进行交互。问题是,当我尝试使用那个 API 时,客户抱怨 system:32
错误,这代表管道损坏。但是,如果我从 class 内部调用该函数,一切都会完美无缺。
#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 确认已准备好处理客户端发送的任何内容。客户端发送用户和密码。
然而,正如我上面所说,它似乎只有在我通过 handle_connect
调用 identify_client
时才起作用,这是完全无用且静态的。我做错了什么?
非常感谢您。
编辑: 消息 class 只是一个 class 与 this one 非常相似,可以帮助我操纵和轻松解释协议的消息.
EDIT2: 我会试着解释得更好一点。我认为 main
函数 "outside the class" 是独立的。 main
函数然后创建一个 Client
对象,其中包含 API 以从服务器发送和接收数据——我附上了 .hpp 文件希望这能帮助你更好地理解——.
因此,如果我有一个像下面这样的循环...
/* ... */
for(;;)
{
/* ... */
// Do something here
/* If a condition, then authenticate the user */
//client.identify_user("User", "Password");
// Do something there
/* ... */
}
/* ... */
...使用创建的对象我应该能够调用对象的特定成员函数,这将触发进一步的写入或读取。我现在遇到的问题是,我无法做到这一点。
如果我从 Client
class 的 handle_connect(...)
函数调用 identify_user
- 如上面的原始代码所示 - 它有效。但是当然,我不能达到我想要的。
如果我评论这个以便从 main
调用函数,那么我会得到 broken pipe 错误。
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");
}
您正在通过对等方已关闭的连接发送数据。
不清楚为什么您希望在任何其他时间对客户端进行身份验证,而不是在建立连接后立即进行身份验证。
我正在尝试实现一个简单的客户端-服务器应用程序,其中我将为客户端引入一个简单的 API 以便与服务器进行交互。问题是,当我尝试使用那个 API 时,客户抱怨 system:32
错误,这代表管道损坏。但是,如果我从 class 内部调用该函数,一切都会完美无缺。
#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 确认已准备好处理客户端发送的任何内容。客户端发送用户和密码。
然而,正如我上面所说,它似乎只有在我通过 handle_connect
调用 identify_client
时才起作用,这是完全无用且静态的。我做错了什么?
非常感谢您。
编辑: 消息 class 只是一个 class 与 this one 非常相似,可以帮助我操纵和轻松解释协议的消息.
EDIT2: 我会试着解释得更好一点。我认为 main
函数 "outside the class" 是独立的。 main
函数然后创建一个 Client
对象,其中包含 API 以从服务器发送和接收数据——我附上了 .hpp 文件希望这能帮助你更好地理解——.
因此,如果我有一个像下面这样的循环...
/* ... */
for(;;)
{
/* ... */
// Do something here
/* If a condition, then authenticate the user */
//client.identify_user("User", "Password");
// Do something there
/* ... */
}
/* ... */
...使用创建的对象我应该能够调用对象的特定成员函数,这将触发进一步的写入或读取。我现在遇到的问题是,我无法做到这一点。
如果我从 Client
class 的 handle_connect(...)
函数调用 identify_user
- 如上面的原始代码所示 - 它有效。但是当然,我不能达到我想要的。
如果我评论这个以便从 main
调用函数,那么我会得到 broken pipe 错误。
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");
}
您正在通过对等方已关闭的连接发送数据。
不清楚为什么您希望在任何其他时间对客户端进行身份验证,而不是在建立连接后立即进行身份验证。