是什么导致async_write错误的文件描述符错误?

时间:2020-06-08 12:42:24

标签: c++ sockets asynchronous boost boost-asio

我在客户端服务器应用程序中使用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";
      }
  };
};

1 个答案:

答案 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),而其他代码也将其关闭(例如,因为它拥有所有权并进行了错误处理)。

    < / li>
  • 更新或,这可能意味着您的套接字从未初始化过才能开始。在您的代码中,我读到:

    //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从不返回值

想象中的修复

这是我想象的消除上述问题时周围代码的样子。

实际上,由于响应的静态性质,它可能要简单得多,但是我不想假设所有响应都那么简单,所以我采用了共享指针生命周期:

Live On Coliru

#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