我在客户端服务器应用程序中使用boost asio并遇到了这个问题,它没有那么多翔实的错误消息(至少对我来说;)),我正在将结构作为消息来回发送,发送工作得很好从客户端,但是从服务器端几乎类似的尝试导致了此问题(错误):Send failed: Bad file descriptor
这是发送部分的代码段(请询问注释中要求的其他详细信息):
void read_from_ts(const char* buf, int len) { // this is the read callback function
if (len <= 0) {
std::cerr << "Error: Connection closed by peer. " << __FILE__ << ":" << __LINE__ << std::endl;
tcp_client_.close(tcp_connection_);
tcp_connection_ = nullptr;
ios_.stop(); // exit
return;
}
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
int tempId = obj->MessageHeaderIn.TemplateID;
Responses r;
switch(tempId)
{
case 10018: //login
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
//std::cout<<"Login request received"<<"\n";
boost::asio::ip::tcp::socket sock_(ios_);
r.login_ack(sock_);
/*will add more*/
}
std::cout << "RX: " << len << " bytes\n";
}
class Responses
{
public:
int login_ack(boost::asio::ip::tcp::socket& socket)
{
//std::cout<<"here"<<"\n";
UserLoginResponse info;
MessageHeaderOutComp mh;
ResponseHeaderComp rh;
rh.MsgSeqNum = 0; //no use for now
rh.RequestTime = 0; //not used at all
mh.BodyLen = 53; //no use
mh.TemplateID = 10019; // IMP
info.MessageHeaderOut = mh;
info.LastLoginTime = 0;
info.DaysLeftForPasswdExpiry = 10; //not used
info.GraceLoginsLeft = 10; //not used
rh.SendingTime = 0;
info.ResponseHeader = rh;
//Pad6 not used
async_write(socket, boost::asio::buffer(&info, sizeof(info)), on_send_completed);
}
static void on_send_completed(boost::system::error_code ec, size_t bytes_transferred) {
if (ec)
std::cout << "Send failed: " << ec.message() << "\n"; //**error shows up here**
else
std::cout << "Send succesful (" << bytes_transferred << " bytes)\n";
}
};
};
答案 0 :(得分:1)
更新刚刚在阅读代码时注意到第三个琐碎的解释,请参阅添加的项目符号
通常在其他位置关闭文件描述符时。
如果您使用的是Asio,则通常意味着
socket
¹对象被破坏。当代码在异步操作期间没有延长对象的寿命时,这可能是一个初学者的错误
文件描述符已传递给其他关闭它的代码(例如,使用native_handle(https://www.boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/basic_stream_socket/native_handle.html)
,而其他代码也将其关闭(例如,因为它拥有所有权并进行了错误处理)。
更新或,这可能意味着您的套接字从未初始化过才能开始。在您的代码中,我读到:
//std::cout<<"Login request received"<<"\n";
boost::asio::ip::tcp::socket sock_(ios_);
r.login_ack(sock_);
但是,这只是构造一个新的套接字,从不连接或绑定该套接字,而是尝试对其执行login_ack
。这不会起作用,因为login_ack
不会绑定或连接套接字并在其上调用async_write
。
您是要使用
tcp_connection_.sock_
还是类似的字词?
通常,在第三方代码中关闭文件描述符是多线程代码中的错误,因为它会引发竞争条件,从而导致任意流损坏(例如,How do you gracefully select() on sockets that could be closed on another thread?)
在大多数情况下,您可以使用
shutdown
代替
还请注意
info
没有足够的生存期(async_write
完成之前就超出了范围login_ack
从不返回值这是我想象的消除上述问题时周围代码的样子。
实际上,由于响应的静态性质,它可能要简单得多,但是我不想假设所有响应都那么简单,所以我采用了共享指针生命周期:
#include <boost/asio.hpp>
#include <boost/core/ignore_unused.hpp>
#include <iostream>
using boost::asio::ip::tcp;
struct MyProgram {
boost::asio::io_context ios_;
struct UserLoginRequest {
struct MessageHeaderInComp {
int TemplateID = 10018;
} MessageHeaderIn;
};
struct Connection {
tcp::socket sock_;
template <typename Executor>
Connection(Executor ex) : sock_{ex} {}
};
std::unique_ptr<Connection> tcp_connection_ = std::make_unique<Connection>(ios_.get_executor());
struct {
void close(std::unique_ptr<Connection> const&);
} tcp_client_;
struct Responses {
static auto login_ack() {
struct UserLoginResponse {
struct MessageHeaderOutComp {
int BodyLen = 53; // no use
int TemplateID = 10019; // IMP
} MessageHeaderOut;
int LastLoginTime = 0;
int DaysLeftForPasswdExpiry = 10; // not used
int GraceLoginsLeft = 10; // not used
struct ResponseHeaderComp {
int MsgSeqNum = 0; // no use for now
int RequestTime = 0; // not used at all
int SendingTime = 0;
} ResponseHeader;
};
return std::make_shared<UserLoginRequest>();
}
};
void read_from_ts(const char* buf, int len) { // this is the read callback function
if (len <= 0) {
std::cerr << "Error: Connection closed by peer. " << __FILE__ << ":" << __LINE__ << std::endl;
tcp_client_.close(tcp_connection_);
tcp_connection_ = nullptr;
ios_.stop(); // exit
return;
}
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
int tempId = obj->MessageHeaderIn.TemplateID;
switch(tempId) {
case 10018: //login
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
//std::cout<<"Login request received"<<"\n";
boost::asio::ip::tcp::socket sock_(ios_);
auto response = Responses::login_ack();
async_write(tcp_connection_->sock_, boost::asio::buffer(response.get(), sizeof(*response)),
[response](boost::system::error_code ec, size_t bytes_transferred) {
if (ec)
std::cout << "Send failed: " << ec.message() << "\n"; //**error shows up here**
else
std::cout << "Send succesful (" << bytes_transferred << " bytes)\n";
});
/*will add more*/
boost::ignore_unused(obj);
}
std::cout << "RX: " << len << " bytes\n";
}
};
int main() {
MyProgram p;
}
¹(或acceptor
/ posix::strean_descriptor
)