无法检测到C++同步提升::asio::ip::tcp::套接字连接正在关闭
Unable to detect C++ synchronous boost::asio::ip::tcp::socket connection being closed
我正在使用boost::asio
与在同一台计算机上运行的节点.js TCP服务器应用程序建立同步TCP套接字连接。我在Windows上使用Embarcadero RAD studio XE4构建64位应用程序,该应用程序使用Embarcadero集成增强版本1.50。
除了节点.js TCP服务器关闭外,一切正常。发生这种情况时,我的C++客户端应用程序在从套接字读取时没有检测到断开连接。但是,当我写入套接字时,它确实会检测到断开连接。
我当前的代码是在尝试理解 BOOST 文档和 SO 上的各种答案后编写的。代码的读取部分如下(为了紧凑性,我省略了对错误代码的检查)
boost::system::error_code ec;
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket s(io_service);
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"),m_port);
ec = s.connect(ep,ec);
std::size_t bytes_available = s.available(ec);
std::vector<unsigned char> data(bytes_available,0);
size_t read_len = boost::asio::read(s, boost::asio::buffer(data),boost::asio::transfer_at_least(bytes_available),ec);
if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec))
{
// disconnection
break;
}
这不是一个很棒的系统,因为它在循环内自己的线程中运行并不断轮询数据,直到程序关闭。通常,当没有数据时,我不会对套接字执行read()
,但在这种情况下我会这样做,因为所有文档都让我相信只有在对套接字执行读取或写入时才会检测到套接字断开连接。问题在于,当节点.js应用程序关闭时,上面的代码根本无法检测到断开连接。如果我在写,我会检测到它(代码的写入部分使用与读取检测相同的 boost::asio::error' 错误),但在读取时没有。
无法对大于可用字节量执行读取,否则我的线程将阻塞,并且我以后将无法在线程循环中执行写入。
我是否缺少另一个特定的提升错误代码来检测错误条件?还是专门针对零长度读取的问题。如果是这种情况,我还有其他选择吗?
目前,我正在让节点.js服务器在关闭时向套接字写出特定消息,我正在检测该消息,然后自己关闭客户端。但是,这有点黑客,如果可能的话,我更喜欢一种干净的方法来检测断开连接。
一般来说,Boost.Asio 的 read()
函数在以下任一情况下返回:
- 缓冲区已满。 满足
- 完整条件。
- 发生错误。
具体说明检查这些条件的顺序。 但是,上次我查看实现时,Boost.Asio 在尝试从套接字读取之前将流上读取零字节的操作视为无操作。 因此,不会观察到文件结束错误,因为 0 大小的缓冲区被视为已满。
虽然使用异步操作可能会提供更好的结果和可伸缩性,但仍可以通过同步操作以非阻塞方式检测断开连接。 默认情况下,同步操作处于阻塞状态,但可以通过 socket::non_blocking()
功能。 文档指出:
如果
true
,套接字的同步操作将失败,如果它们无法立即执行请求的操作,则boost::asio::error::would_block
。如果false
,同步操作将阻塞,直到完成。
因此,如果同步操作设置为不阻塞,并且读取操作尝试读取至少 1 个字节,则可以以非阻塞方式观察到断开连接。
下面是一个完整的示例,演示了检测断开连接的非阻塞同步read()
操作。 为了限制打印消息,我选择在操作被阻止时执行睡眠(即有连接但没有数据可供读取)。
#include <algorithm>
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: <port>n";
return 1;
}
// Create socket and connet to local port.
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
ip::tcp::socket socket(io_service);
socket.connect(ip::tcp::endpoint(
ip::address::from_string("127.0.0.1"), std::atoi(argv[1])));
// By setting the socket to non-blocking, synchronous operations will
// fail with boost::asio::error::would_block if they cannot immediately
// perform the requested operation.
socket.non_blocking(true);
// Synchronously read data.
std::vector<char> data;
boost::system::error_code ec;
for (;;)
{
// Resize the buffer based on the amount of bytes available to be read.
// Guarantee that the buffer is at least 1 byte, as Boost.Asio treats
// zero byte read operations as no-ops.
data.resize(std::max<std::size_t>(1, socket.available(ec)));
// Read all available data.
std::size_t bytes_transferred =
boost::asio::read(socket, boost::asio::buffer(data), ec);
// If no data is available, then continue to next iteration.
if (bytes_transferred == 0 && ec == boost::asio::error::would_block)
{
std::cout << "no data available" << std::endl;
boost::this_thread::sleep_for(boost::chrono::seconds(3));
continue;
}
std::cout << "Read: " << bytes_transferred << " -- ";
if (bytes_transferred)
{
std::cout.write(&data[0], bytes_transferred);
std::cout << " -- ";
}
std::cout << ec.message() << std::endl;
// On error, such as a disconnect, exit the loop.
if (ec && ec != boost::asio::error::would_block)
{
break;
}
}
}
通过将示例程序连接到写入"test","more testing"的服务器,然后关闭连接,产生了以下输出:
no data available
Read: 4 -- test -- Success
no data available
Read: 12 -- more testing -- Success
Read: 0 -- End of file
- 当套接字连接断开时检测C/C++Unix
- 套接字连接"Operation not permitted"错误,甚至使用升压/平发器根.cpp
- C++套接字客户端到 Python 服务器未创建连接
- 在不知道套接字的情况下关闭网络连接
- 当对套接字 send() 的同步调用由于连接另一端丢失而被阻止时,如何恢复?
- 当客户端在 write() 期间终止连接时,由对等套接字错误重置连接
- 从网链套接字请求连接设备的列表
- C++ TCP 套接字通信 - 连接按预期工作,几秒钟后失败,没有收到新数据,read() 和 recv() 块
- 如何在 2 台主机之间保持 UDP 套接字连接打开
- C++关闭套接字以启动新连接
- Opencv 不适用于套接字连接
- 使用单个套接字处理多个传入的 UDP 连接
- 连接UDP套接字,但仍然接收来自其他源的数据报
- C++通过套接字连接发送矢量
- 为什么我在蓝牙连接()上收到"java.io.IOException:读取失败,套接字可能关闭或超时,读取re
- boost1.62 在 docker 容器中重新连接后套接字损坏
- 尝试连接到 TCP 套接字 (Linux) 时连接被拒绝
- 仅通过建立一次TCP连接将Recv从客户端发送到服务器套接字
- 接受套接字,但m_socket.远程终结点引发 传输终结点未连接
- 提振.Asio:对每个连接/套接字使用“io_service”是件好事吗?