c++ - boost asio async_read header 连接过早关闭

标签 c++ boost boost-asio

提供 MCVE会很难,场景如下:

  • 用 c++ 编写的带有 boost asio 的服务器提供了一些服务
  • 用 c++ 编写的带有 boost asio 请求服务的客户端

有自定义 header ,大多数通信是使用 multipart/form 完成的。 但是,在服务器返回 401 表示未授权访问的情况下, 客户端收到损坏的管道(系统错误 32)。

据我所知,当服务器连接过早关闭时会发生这种情况。 所以,运行到 gdb,我可以看到问题确实是从发送请求的 async_write 到读取 HTTP header 第一行的 async_read_until 的转换:

connect 例程将请求从客户端发送到服务器:

    boost::asio::async_write(*socket_.get(),
                             request_,
                             boost::bind(&asio_handler<http_socket>::write_request, 
                                         this,
                                         boost::asio::placeholders::error,
                                         boost::asio::placeholders::bytes_transferred));

write_request 回调,检查请求是否发送成功,然后读取第一行(直到第一个换行符):

template <class T> 
void asio_handler<T>::write_request(const boost::system::error_code & err,
                                    const std::size_t bytes)
{       
    if (!err) {
        // read until first newline
        boost::asio::async_read_until(*socket_,
                                      buffer_,
                                      "\r\n",
                                      boost::bind(&asio_handler::read_status_line, 
                                                  this, 
                                                  boost::asio::placeholders::error,
                                                  boost::asio::placeholders::bytes_transferred));
    }
    else {
        end(err);       
    }
}

问题是 end(err) 总是用损坏的管道调用(错误代码 32)。据我了解,这意味着服务器关闭了连接。服务器确实关闭了连接,但只是它发送消息 HTTP/1.1 401 Unauthorized 之后。

  • 在适当的请求中使用 curl,我们确实在服务器关闭连接之前得到了实际的消息/错误
  • 使用我们用 C++/boost asio 编写的客户端,我们只得到破损的管道而没有数据
  • 只有当服务器保持连接打开时,我们才能读取错误 (401),但这违背了目的,因为现在连接保持打开状态。

如果有任何提示或技巧,我将不胜感激。我知道没有代码很难提供帮助,所以我可以随时添加更多源。

编辑:

如果我检查写入请求和读取服务器回复之间的错误,那么我会得到实际的 HTTP 401 错误。然而,这似乎违反直觉,我不确定为什么会发生这种情况,或者它是否应该发生。

最佳答案

根据 HTTP 规范,观察到的行为是允许的。

客户端或服务器可以随时关闭套接字。服务器可以在客户端完成传输请求之前提供响应并关闭连接。编写正文时,建议客户端监视套接字是否有错误或关闭通知。来自RFC 7230, HTTP/1.1: Message Syntax and Routing Section 6.5. Failures and Timeouts :

6.5. Failures and Timeouts

A client, server, or proxy MAY close the transport connection at any time. [...]

A client sending a message body SHOULD monitor the network connection for an error response while it is transmitting the request. If the client sees a response that indicates the server does not wish to receive the message body and is closing the connection, the client SHOULD immediately cease transmitting the body and close its side of the connection.

在优雅的连接关闭时,服务器将在关闭底层套接字之前向客户端发送响应:

6.6. Tear-down

A server that sends a "close" connection option MUST initiate a close of the connection [...] after it sends the response containing "close". [...]

鉴于上述行为,存在三种可能的情况。 async_write() 操作完成:

  • 成功,表示请求已完整写入。客户端可能还没有收到 HTTP 响应
  • 一个错误,表明请求没有写完整。如果套接字上有数据可供读取,那么它可能包含服务器在连接终止之前发送的 HTTP 响应。 HTTP 连接可能已正常终止
  • 一个错误,表明请求没有写完整。如果套接字上没有可供读取的数据,则 HTTP 连接未正常终止

请考虑:

  • 如果 async_write() 成功或有数据可供读取,则启动 async_read() 操作

     void write_request(
       const boost::system::error_code & error,
       const std::size_t bytes_transferred)
     {
       // The server may close the connection before the HTTP Request finished
       // writing.  In that case, the HTTP Response will be available on the
       // socket.  Only stop the call chain if an error occurred and no data is
       // available.
       if (error && !socket_->available()) 
       {
         return;
       }
    
       boost::asio::async_read_until(*socket_, buffer_, "\r\n", ...);
    }
    
  • 根据 RFC 建议,在 async_write() 的同时启动 async_read() 操作。如果服务器指示 HTTP 连接正在关闭,则客户端将关闭其套接字的发送端。额外的状态处理可能不会保证额外的复杂性

关于c++ - boost asio async_read header 连接过早关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41165255/

相关文章:

c++ - 为什么 cvPutText 在这种情况下不起作用?

c++ - vector 'no operator "[Visual Studio watch 中的 ]"matches these operands' 错误

c++ - 未在此范围内声明 C++

c++ - 对于 boost io_service,是否只有一个线程在 epoll_wait 上阻塞?

c++ - 使用 C++/boost::asio 在网络框架中进行 int/char 转换

c++ - 为什么编译器不将此标记为错误而不是警告?

c++ - 使用boost::thread的Actor计算模型

boost - 无法使用基于 qnx7.0 的 qcc 为 boost_1.68.0 构建 libboost_filesystem.a

c++ - boost::heap::priority_queue 与 std::priority_queue 的比较器

c++ - “Strange”内存泄漏-TCP网络