сервер.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
Я пытался следовать этому Как получить пользовательский тип данных из чтения сокета? , но это не помогает.





вы портите размер буфера при чтении
boost::asio::read(s, boost::asio::buffer(&data, sizeof(data)));
неправильно, вероятно, должно быть:
boost::asio::read(s, boost::asio::buffer(data));
вы передаете размер векторного объекта, а не размер векторного хранилища.
Кстати, с повышением вам не нужно устанавливать размер, поскольку он может считывать его из самого вектора.
ПРИМЕЧАНИЕ: ваш код все равно не будет работать, потому что вы на самом деле отправляете целое число 9 и пытаетесь получить 10, поэтому вы получите исключение «конец файла» при чтении, но это другая история.
Эта работа, которую я пробовал сначала, и она не работает, и это потому, что я, вероятно, не посчитал количество целых чисел в векторе, просто небольшая ошибка по невнимательности, извините. Теперь он работает нормально. Но могу ли я узнать, как получить вектор неизвестного размера?
вы можете использовать другую версию чтения, которая использует CompletionCondition, например
"Но могу ли я знать, как получить вектор неизвестного размера?" - использовать сериализацию (Boost Serialization, json или что-то еще) и включать протокол кадрирования сообщений
Я пошел дальше и показал, как можно использовать Boost Serialization для отправки/получения произвольно сложных структур данных.
"Но могу ли я знать, как получить вектор неизвестного размера?" - использовать сериализацию (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); }
#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
Вы читаете саму векторную структуру, а не данные, если они есть, попробуйте построить буфер на клиенте, как вы это делаете на сервере.