来自 boost::asio::ip::tcp::socket 个奇怪行为的字符串

String from boost::asio::ip::tcp::socket weird behaviour

我正在尝试实现一个使用 C++ 套接字的应用程序。我正在尝试从套接字中读取字符串,但遇到了各种各样的问题。在main函数中获取不到字符串,但是在函数中可以获取到。谁能指出我正确的方向?

这是我的代码:

int main(int argc, char *argv[]) {

    try {
        asio::io_context io_context;

        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 8080));

        tcp::socket socket(io_context);
        printf("Accepting...\n");
        acceptor.accept(socket);

        for (;;)
        {
            std::string message = read_(socket);
            printf("Message: %s\n", message);
        }
    } catch (std::exception& e) {
        printf(e.what());
    }
}

std::string read_(tcp::socket & socket) {
    asio::streambuf buf;
    asio::read_until( socket, buf, "\n" );
    const unsigned char* data = asio::buffer_cast<const unsigned char*>(buf.data());
    std::string result((char*)data);
    printf("Text: %s, and length: %d\n", result, result.length());
    printf("Text casted: %s\n", data);
    return result;
}

这是我通过套接字发送字符串“test”后的输出:

Accepting...
Text: ♀²w, and length: 6
Text casted: test

Message: ░■w

printf 不将 std::string 作为参数。由于 printf 不是类型安全的并且是可变参数函数,这就是它编译并且不给出任何错误的原因。将 std::string 传递给它将是未定义的行为。

要么使用std::cout,要么使用std::string的c_str方法。

是的,正如其他人所指出的,printf 不适用于 std::string。您可以轻松解决它,但编写 C++ 代码也许是个好主意:

std::cout << "Message: " << message << "\n";

// ...
std::cout << "Text: " << result << ", and length: " << result.length()
          << "\n";
std::cout << "Text casted: " << data << "\n";

除此之外,还有...问题。您有一个从 unsigned char const*char* 的 C 风格转换。 C 风格的转换很危险,因为它们最终会变成 reinterpret_cast-ing。在这种情况下,您错误地(并且不必要地)丢弃了 const.

为什么不只是 buffer_cast 到所需的类型呢?

auto data = asio::buffer_cast<char const*>(buf.data());
std::string result(data);

这给我们带来了下一个问题:您假设数据以 NUL 结尾 并且 不包含任何嵌入的 NUL 字符。更好的方法是使用 asio::read_until 中的 return 值(你忽略了):

auto length = asio::read_until(socket, buf, "\n");

auto data = asio::buffer_cast<char const*>(buf.data());
std::string result(data, data + length);

话虽这么说,但我不相信您可以在没有进一步准备的情况下使用 streambuf 就好像是连续的。为什么不使用缓冲区迭代器来确保您不会误解任何实现细节:

asio::streambuf buf;
/*auto length =*/asio::read_until(socket, buf, "\n");

std::string const result(buffers_begin(buf.data()),
                         buffers_end(buf.data()));

请注意,这会立即消除忘记长度的“问题”。

小费

为什么不读入一个字符串,节省副本和复杂性:

std::string result;
asio::read_until(socket, asio::dynamic_buffer(result), "\n");

固定

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;

std::string read_(tcp::socket& socket);

int main()
{
    try {
        asio::io_context io_context;

        tcp::acceptor acceptor(io_context, {{}, 6768});

        tcp::socket socket(io_context);
        std::cout << "Accepting...\n";
        acceptor.accept(socket);

        for (;;)
        {
            std::string message = read_(socket);
            std::cout << "Message: " << message << "\n";
        }
    } catch (std::exception& e) {
        std::cout << e.what() << "\n";
    }
}

std::string read_(tcp::socket& socket)
{
    std::string result;
    asio::read_until(socket, asio::dynamic_buffer(result), "\n");
    return result;
}