async_write 导致错误文件描述符错误的原因是什么?
What causes bad file descriptor error with async_write?
我正在将 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";
}
};
};
更新 刚刚在阅读您的代码时注意到第三个简单的解释,请参阅添加的项目符号
通常在其他地方关闭文件描述符时。
如果您使用的是 Asio,这通常意味着
socket
¹ 对象已被破坏。当代码在异步操作期间不延长对象的生命周期时,这可能是初学者错误
文件描述符被传递给关闭它的其他代码(例如使用 native_handle(https://www.boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/basic_stream_socket/native_handle.html)
并且其他代码关闭它(例如因为它假定所有权并进行了错误处理)。
UPDATE 或者,这可能意味着您的套接字从未初始化开始。在你的代码中我读到:
//std::cout<<"Login request received"<<"\n";
boost::asio::ip::tcp::socket sock_(ios_);
r.login_ack(sock_);
然而,这只是构造一个新套接字,从不连接或绑定它并尝试对其执行 login_ack
。那是行不通的,因为 login_ack
既不绑定也不连接套接字并在其上调用 async_write
。
Did you mean to use tcp_connection_.sock_
or similar?
In general closing file-descriptors in third-party code is an error in multi-threaded code because it invites race conditions which will lead to arbitrary stream corruption (see e.g. How do you gracefully select() on sockets that could be closed on another thread?)
You can use shutdown
instead in the majority of cases
未定义的行为
另外,请注意
info
没有足够的生命周期(它在 async_write
完成之前超出范围
- 你的
login_ack
从来没有returns一个值
想象修复
这是我想象的周围代码在解决上述问题后的样子。
事实上,由于响应的静态性质,它可能会简单得多,但我不想假设所有响应都那么简单,所以我选择了共享指针生命周期:
#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
)
我正在将 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";
}
};
};
更新 刚刚在阅读您的代码时注意到第三个简单的解释,请参阅添加的项目符号
通常在其他地方关闭文件描述符时。
如果您使用的是 Asio,这通常意味着
socket
¹ 对象已被破坏。当代码在异步操作期间不延长对象的生命周期时,这可能是初学者错误文件描述符被传递给关闭它的其他代码(例如使用
native_handle(https://www.boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/basic_stream_socket/native_handle.html)
并且其他代码关闭它(例如因为它假定所有权并进行了错误处理)。UPDATE 或者,这可能意味着您的套接字从未初始化开始。在你的代码中我读到:
//std::cout<<"Login request received"<<"\n"; boost::asio::ip::tcp::socket sock_(ios_); r.login_ack(sock_);
然而,这只是构造一个新套接字,从不连接或绑定它并尝试对其执行
login_ack
。那是行不通的,因为login_ack
既不绑定也不连接套接字并在其上调用async_write
。Did you mean to use
tcp_connection_.sock_
or similar?
In general closing file-descriptors in third-party code is an error in multi-threaded code because it invites race conditions which will lead to arbitrary stream corruption (see e.g. How do you gracefully select() on sockets that could be closed on another thread?)
You can use
shutdown
instead in the majority of cases
未定义的行为
另外,请注意
info
没有足够的生命周期(它在async_write
完成之前超出范围- 你的
login_ack
从来没有returns一个值
想象修复
这是我想象的周围代码在解决上述问题后的样子。
事实上,由于响应的静态性质,它可能会简单得多,但我不想假设所有响应都那么简单,所以我选择了共享指针生命周期:
#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
)