服务器.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;
}
}
我期望的:我希望服务器.cpp向客户端发送 int {} 的向量
std::vector<int> vc = {1, 2, 3, 4, 5, 6, 7, 8, 9};
然后.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,因此您将从读取中获得"文件结束"异常,但这是另一回事。
"但是我能知道如何接收未知大小的向量吗?" - 使用 序列化(Boost Serialization,json或其他任何)并包括一个 消息框架协议 – SEHE 5小时前
因为我相信"显示,不要告诉",所以下面是一个示例,它使用 C++20 和 Boost 序列化来实现数据回显服务,该服务将以下结构发送(和返回):
struct ApplicationData {
std::vector<int> ints;
std::map<std::string, double> map;
};
序列化
对于序列化,提供:
void serialize(auto& ar, unsigned) { ar & ints & map; }
Boost 序列化知道如何处理字符串、映射和向量。
我们还让我们在给定对象的情况下轻松使用 Boost 序列化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");
}
协程
使用一些方便的defs,我们可以快速编写协程:
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