C++ boost asio: простой сервер/клиент с boost:asio::read и boost::asio::write, передающий вектор int

сервер.cpp

#include <boost/asio.hpp>
#include <vector>
using boost::asio::ip::tcp;

int main()
{
    boost::asio::io_service ctx;
    std::vector<int> vc = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    tcp::acceptor s(ctx, tcp::endpoint({}, 1234));
    tcp::socket conn = s.accept();
    boost::asio::write(conn, boost::asio::buffer(vc));
} 

клиент.cpp

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

int main()
{
    boost::asio::io_service ctx;
    tcp::socket s(ctx);
    s.connect(tcp::endpoint({}, 1234));
    std::vector<int> data(10);
    boost::asio::read(s, boost::asio::buffer(&data, sizeof(data)));

    for (auto x : data)
    {
        std::cout << x;
    }
}

Что я ожидал: я ожидаю, что server.cpp отправит клиенту вектор int {}

std::vector<int> vc = {1, 2, 3, 4, 5, 6, 7, 8, 9};

Затем client.cpp получит его, сохранит в data и распечатает.

Текущий результат: печатается случайный результат, программа не завершается (бесконечно). Некоторые вещи копируются из командной строки, но это не останавливается

26710220016661817222889274343557-214234613021914980240162615587848787224662874348677-17396929467224662874344069-204168283435745930074342533275669381911258937235205-10518278365142660544104632-2123472756944701572-531734500513653821913629-431025833424607876961438854961439111-1605430441513807051429161632526724-514957158-1286708961-1722871465961441157961440647-10517823135587861975587910445420122923239035401615725572156135866-921828804-53346303354091785516346447661095676702-529630690162195379954202857416346304291095676702-5296301791134131916325264895177476-53175062851527985158940-514916202558825605-428968316-244381721-1052917621558784836644769668-2041704702-2039585146-244387042-1972796771370310219-227626210-1841446849-244403426-240316597-1972796411370309963-227626210-1841446813-244403426-944959413-244387041-23083408513630013831919857438-1303465186-1536266722-2276098271689063955722665261701735454-46249085116722869991632510750-4814189801664991319558789404-246504676163873306321934878-512852195-508750817540917819-4289364201756172547-164364287-173190433-491957361-18996792912092702721463582721756954624676495360-2143748096148180172812092702759437233-398605863227607747-1588838227121307139359768881-1556233763-1269955807-1049730683-445750395-398606325110167107-1488174931-95114723612151757815976888

Я пытался следовать этому Как получить пользовательский тип данных из чтения сокета? , но это не помогает.

Вы читаете саму векторную структуру, а не данные, если они есть, попробуйте построить буфер на клиенте, как вы это делаете на сервере.

Alan Birtles 28.12.2022 14:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

вы портите размер буфера при чтении

boost::asio::read(s, boost::asio::buffer(&data, sizeof(data)));

неправильно, вероятно, должно быть:

boost::asio::read(s, boost::asio::buffer(data));

вы передаете размер векторного объекта, а не размер векторного хранилища.

Кстати, с повышением вам не нужно устанавливать размер, поскольку он может считывать его из самого вектора.

ПРИМЕЧАНИЕ: ваш код все равно не будет работать, потому что вы на самом деле отправляете целое число 9 и пытаетесь получить 10, поэтому вы получите исключение «конец файла» при чтении, но это другая история.

Эта работа, которую я пробовал сначала, и она не работает, и это потому, что я, вероятно, не посчитал количество целых чисел в векторе, просто небольшая ошибка по невнимательности, извините. Теперь он работает нормально. Но могу ли я узнать, как получить вектор неизвестного размера?

NOK 28.12.2022 14:28

вы можете использовать другую версию чтения, которая использует CompletionCondition, например

OznOg 28.12.2022 14:34

"Но могу ли я знать, как получить вектор неизвестного размера?" - использовать сериализацию (Boost Serialization, json или что-то еще) и включать протокол кадрирования сообщений

sehe 28.12.2022 17:06

Я пошел дальше и показал, как можно использовать Boost Serialization для отправки/получения произвольно сложных структур данных.

sehe 28.12.2022 22:36

"Но могу ли я знать, как получить вектор неизвестного размера?" - использовать сериализацию (Boost Serialization, json или что-то еще) и включить протокол обработки сообщений — sehe 5 часов назад

Поскольку я верю в принцип «покажи, а не говори», вот пример, в котором используется C++20 с ускоренной сериализацией для реализации службы эха данных, которая отправляет следующую структуру (и обратно):

struct ApplicationData {
    std::vector<int>              ints;
    std::map<std::string, double> map;
};

Сериализация

Для сериализации достаточно поставить:

    void serialize(auto& ar, unsigned) { ar & ints & map; }

Сериализация Boost знает, как работать со строками, картами и векторами.

Давайте также упростим использование Boost Serialization для заданных asio::streambuf объектов:

static constexpr auto FRAME = "FRAME_SEPARATOR"sv;
static inline auto& as_frame(auto const& data, asio::streambuf& buf) {
    std::ostream os(&buf);
    boost::archive::text_oarchive(os) << data;
    os << FRAME << std::flush;
    return buf;
}

template <typename T> static inline T from_frame(asio::streambuf& buf) {
    T obj;
    std::istream is(&buf);
    boost::archive::text_iarchive ia(is);
    ia >> obj;
    if (std::string frame; is >> frame && frame == FRAME)
        return obj;
    throw std::runtime_error("Stream error");
}

Корутины

Используя несколько удобных определений, мы можем написать сопрограммы в один миг:

using Void     = asio::awaitable<void>;
using Acceptor = asio::use_awaitable_t<>::as_default_on_t<tcp::acceptor>;
using Socket   = asio::use_awaitable_t<>::as_default_on_t<tcp::socket>;

Сервер

Сервер может быть вот таким:

Void server(uint16_t port) {
    auto ex = co_await asio::this_coro::executor;
    
    for (Acceptor acc(ex, {{}, port});;)
        co_spawn(ex, echo_data(co_await acc.async_accept()), detached);
}

Конечно, нам нужно реализовать echo_data:

Void echo_data(Socket s) {
    asio::streambuf buf;
    co_await async_read_until(s, buf, FRAME);
    auto request = from_frame<ApplicationData>(buf);

    std::cout << "server echo to " << s.remote_endpoint() << ", " << request << std::endl;
    co_await async_write(s, as_frame(request, buf));
}

Клиент

Теперь пришло время написать код клиента:

Void client(uint16_t port, ApplicationData const data) {
    auto ex = co_await asio::this_coro::executor;
    Socket s(ex);
    co_await s.async_connect({{}, port});

    asio::streambuf buf, received;
    co_await async_write(s, as_frame(data, buf));
    co_await async_read_until(s, received, FRAME);
    auto response = from_frame<ApplicationData>(received);
    std::cout << "client response " << response << std::endl;
    std::cout << "client roundtrip " << std::boolalpha << (response == data) << std::endl;
}

Чтобы разрешить отладочный вывод, мы добавили operator== и operator<< до ApplicationData:

bool operator==(ApplicationData const&) const = default;
friend std::ostream& operator<<(std::ostream& os, ApplicationData const& ad) {
    return os << fmt::format("{{ints:{} complex:{}}}", ad.ints, ad.map);
}

Полная демоверсия В прямом эфире на Coliru

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>

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

#include <fmt/ranges.h>
using namespace std::literals;

struct ApplicationData {
    std::vector<int>              ints;
    std::map<std::string, double> map;

    void serialize(auto& ar, unsigned) { ar & ints & map; }
    bool operator==(ApplicationData const&) const = default;
    friend std::ostream& operator<<(std::ostream& os, ApplicationData const& ad) {
        return os << fmt::format("{{ints:{} complex:{}}}", ad.ints, ad.map);
    }
};

static constexpr auto FRAME = "FRAME_SEPARATOR"sv;
static inline auto& as_frame(auto const& data, asio::streambuf& buf) {
    std::ostream os(&buf);
    boost::archive::text_oarchive(os) << data;
    os << FRAME << std::flush;
    return buf;
}

template <typename T> static inline T from_frame(asio::streambuf& buf) {
    T obj;
    std::istream is(&buf);
    boost::archive::text_iarchive ia(is);
    ia >> obj;
    if (std::string frame; is >> frame && frame == FRAME)
        return obj;
    throw std::runtime_error("Stream error");
}

using Void     = asio::awaitable<void>;
using Acceptor = asio::use_awaitable_t<>::as_default_on_t<tcp::acceptor>;
using Socket   = asio::use_awaitable_t<>::as_default_on_t<tcp::socket>;

Void echo_data(Socket s) {
    asio::streambuf buf;
    co_await async_read_until(s, buf, FRAME);
    auto request = from_frame<ApplicationData>(buf);

    std::cout << "server echo to " << s.remote_endpoint() << ", " << request << std::endl;
    co_await async_write(s, as_frame(request, buf));
}

Void server(uint16_t port) {
    auto ex = co_await asio::this_coro::executor;
    
    for (Acceptor acc(ex, {{}, port});;)
        co_spawn(ex, echo_data(co_await acc.async_accept()), detached);
}

Void client(uint16_t port, ApplicationData const data) {
    auto ex = co_await asio::this_coro::executor;
    Socket s(ex);
    co_await s.async_connect({{}, port});

    asio::streambuf buf, received;
    co_await async_write(s, as_frame(data, buf));
    co_await async_read_until(s, received, FRAME);
    auto response = from_frame<ApplicationData>(received);
    std::cout << "client response " << response << std::endl;
    std::cout << "client roundtrip " << std::boolalpha << (response == data) << std::endl;
}

int main() {
    boost::asio::io_context ioc;
    co_spawn(ioc, server(2233), detached);
    co_spawn(ioc,
             client(2233, {{3, 4, 5}, {{"one third", 1. / 3}, {"one fourth", 1. / 4}}}),
             detached);
    co_spawn(ioc, client(2233, {{42}, {{"LtUaE^2", 1.764e3}}}), detached);

    ioc.run_for(3s);
}

При запуске с

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lfmt -lboost_serialization && ./a.out

Отпечатки

server echo to 127.0.0.1:57782, {ints:[3, 4, 5] complex:{"one fourth": 0.25, "one third": 0.3333333333333333}}
server echo to 127.0.0.1:57784, {ints:[42] complex:{"LtUaE^2": 1764}}
client response {ints:[3, 4, 5] complex:{"one fourth": 0.25, "one third": 0.3333333333333333}}
client roundtrip true
client response {ints:[42] complex:{"LtUaE^2": 1764}}
client roundtrip true

Другие вопросы по теме