我正在使用 boost asio 连接到 TCP 服务器。 当我运行代码时,它在启动后工作正常。我发送请求并得到响应。 当我关闭 tcp 服务器(它是一个设备)时,我遇到超时,并且当我遇到超时时,来自 boost 的 async_read 内部的回调将永远不会被执行。然后我关闭套接字。打开设备后,可以重新建立连接,但接收到的缓冲区大小为 0 字节。我认为那是因为 async_read 在超时后没有正确完成。
标题
#include <iostream>
#include <boost/format.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <chrono>
#include <thread>
#include <future>
#include <sstream>
#include <iomanip>
class TcpClient{
public:
int connect(boost::asio::ip::tcp::socket &socket, boost::asio::ip::tcp::endpoint &endpoint);
int writeAndRead(boost::asio::ip::tcp::socket &socket);
};
这是代码
#include "tcpclient.h"
int TcpClient::connect(boost::asio::ip::tcp::socket &socket, boost::asio::ip::tcp::endpoint &endpoint)
{
boost::system::error_code error;
socket.connect(endpoint, error);
if (!error)
{
std::cout << "connected" << std::endl;
return 1;
}
else
{
std::cout << "not connected" << std::endl;
return 0;
}
}
int TcpClient::writeAndRead(boost::asio::ip::tcp::socket &socket)
{
boost::system::error_code error;
auto status = std::async(std::launch::async, [&]()
{ boost::asio::write(socket, boost::asio::buffer("mytext"), error); })
.wait_for(std::chrono::milliseconds{1000});
switch (status)
{
case std::future_status::deferred:
std::cout << "std::future_status::deferred" << std::endl;
return 0;
case std::future_status::ready:
std::cout << "write success" << std::endl;
break;
case std::future_status::timeout:
std::cout << "std::future_status::timeout" << std::endl;
return 0;
}
boost::asio::streambuf receive_buffer;
boost::optional<boost::system::error_code> read_result;
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(socket.get_io_service());
timer.expires_from_now(boost::posix_time::seconds(2));
timer.async_wait([&timer_result](const boost::system::error_code &error)
{
if (error != boost::asio::error::operation_aborted)
{
timer_result = error;
} });
boost::asio::async_read(socket,
receive_buffer,
boost::asio::transfer_at_least(1),
[&read_result](const boost::system::error_code &ec, std::size_t bytes_transferred)
{
std::cout << "read_result: " << read_result << std::endl;
read_result = ec;
});
boost::system::error_code ec;
while (1)
{
socket.get_io_service().reset();
int numHandlers = socket.get_io_service().poll_one(ec);
if (read_result)
{
timer.cancel();
break;
}
else if (timer_result)
{
timer.cancel();
std::cout << "timeout" << std::endl;
return 0;
}
}
if (receive_buffer.size() == 0)
{
std::cout << "receive_buffer size 0" << std::endl;
return 0;
}
std::string rawResponse = boost::asio::buffer_cast<const char *>(receive_buffer.data());
std::cout << "rawResponse: " << rawResponse << std::endl;
return 1;
}
int main()
{
std::string ipAddress = "192.168.2.4";
unsigned short port = 50001;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ipAddress), port);
boost::asio::io_service ioService;
boost::asio::ip::tcp::socket socket{ioService};
bool isSuccess{false};
bool isConnected{false};
TcpClient tcpclient = TcpClient();
while (1)
{
if (!isConnected)
{
isConnected = tcpclient.connect(socket, endpoint);
}
if (isConnected)
{
isSuccess = tcpclient.writeAndRead(socket);
if (!isSuccess)
{
std::cout << "failed close socket" << std::endl;
socket.close();
isConnected = false;
}
else
{
std::cout << "success" << std::endl;
}
}
std::cout << "wait for 1 sec" << std::endl;
std::chrono::seconds dura(1);
std::this_thread::sleep_for(dura);
}
return 0;
}
这是输出
success
wait for 1 sec
write success
read_result: 0
rawResponse: 0;
success
wait for 1 sec
write success
timeout
failed close socket
wait for 1 sec
not connected
wait for 1 sec
not connected
wait for 1 sec
not connected
wait for 1 sec
not connected
wait for 1 sec
connected
write success
read_result: 0
receive_buffer size 0
failed close socket
wait for 1 sec
connected
write success
read_result: 0
receive_buffer size 0
failed close socket
Asio是一个异步IO库。
您正在使用带有 std::async 的阻塞 API 来...假异步 IO。我建议不要这样做。看起来您的代码是基于(非常)旧的示例代码。
get_io_service()
早已不复存在,io_service
本身仅作为已弃用 API 的一部分提供。
我不知道你可以访问什么编译器,但这就是我现在编写相同代码的方式:
#include <boost/asio.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using Socket = asio::use_awaitable_t<>::as_default_on_t<tcp::socket>;
using namespace std::chrono_literals;
using namespace asio::experimental::awaitable_operators;
asio::awaitable<void> delay(std::chrono::steady_clock::duration dur) {
auto ex = co_await asio::this_coro::executor;
co_await asio::steady_timer(ex, dur).async_wait(asio::deferred);
}
asio::awaitable<void> requestLoop(Socket& socket) {
for (std::string request = "test", response;;) {
if (auto r = co_await (async_write(socket, asio::buffer(request)) || delay(1s)); r.index()) {
std::cout << "write timeout" << std::endl;
break;
}
auto r = co_await ( //
asio::async_read(socket, asio::dynamic_buffer(response), asio::transfer_at_least(1),
as_tuple(asio::use_awaitable)) ||
delay(2s));
if (r.index()) {
std::cout << "read timeout" << std::endl;
break;
}
auto [ec, bytes] = get<0>(r);
std::cout << "Response: " << quoted(response) << " (" << ec.message() << ")" << std::endl;
}
}
asio::awaitable<void> client(std::string ipAddress, uint16_t port) {
auto ex = co_await asio::this_coro::executor;
for (;; co_await delay(1s)) {
try {
Socket socket(ex);
co_await socket.async_connect({asio::ip::address::from_string(ipAddress), port});
std::cout << "Connected to " << socket.remote_endpoint() << std::endl;
co_await requestLoop(socket);
} catch (boost::system::system_error const& se) {
std::cout << "Error: " << se.code().message() << std::endl;
}
}
}
int main() {
asio::io_context ioc;
//co_spawn(ioc, client("192.168.1.1", 8045), asio::detached);
co_spawn(ioc, client("0.0.0.0", 8045), asio::detached);
ioc.run();
}
本地演示: