【发布时间】:2021-10-14 12:24:09
【问题描述】:
我正在尝试为将使用 Boost ASIO 构建的项目实现一个简单的 IPC 协议。这个想法是通过 IP/TCP 进行通信,服务器带有后端,客户端将使用从服务器接收到的数据来构建前端。整个会话会这样进行:
- 连接已建立
- 客户端发送一个 2 字节的数据包,其中包含一些信息,服务器将使用这些信息来构建其响应(存储为结构
propertiesPacket) - 服务器处理接收到的数据并将输出存储在一个名为
processedData的可变大小结构中 - 服务器发送一个 2 字节的无符号整数,指示客户端它将接收到的结构的大小(假设结构的大小为
n字节) - 服务器将结构数据作为
n字节包发送 - 连接已结束
我尝试自己实现这一点,遵循 Boost ASIO 文档中的精彩教程,以及库中包含的示例和我在 Github 上找到的一些存储库,但由于这是我第一次使用网络和 IPC,我无法让它工作,我的客户端返回一个异常,说连接已被对等方重置。
我现在拥有的是这样的:
// File client.cpp
int main(int argc, char *argv[])
{
try {
propertiesPacket properties;
// ...
// We set the data inside the properties struct
// ...
boost::asio::io_context io;
boost::asio::ip::tcp::socket socket(io);
boost::asio::ip::tcp::resolver resolver(io);
boost::asio::connect(socket, resolver.resolve(argv[1], argv[2]));
boost::asio::write(socket, boost::asio::buffer(&properties, sizeof(propertiesPacket)));
unsigned short responseSize {};
boost::asio::read(socket, boost::asio::buffer(&responseSize, sizeof(short)));
processedData* response = reinterpret_cast<processedData*>(malloc(responseSize));
boost::asio::read(socket, boost::asio::buffer(response, responseSize));
// ...
// The client handles the data
// ...
return 0;
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
}
}
// File server.cpp
class ServerConnection
: public std::enable_shared_from_this<ServerConnection>
{
public:
using TCPSocket = boost::asio::ip::tcp::socket;
ServerConnection::ServerConnection(TCPSocket socket)
: socket_(std::move(socket)),
properties_(nullptr),
filePacket_(nullptr),
filePacketSize_(0)
{
}
void start() { doRead(); }
private:
void doRead()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(properties_, sizeof(propertiesPacket)),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec) {
processData();
doWrite(&filePacketSize_, sizeof(short));
doWrite(filePacket_, sizeof(*filePacket_));
}
});
}
void doWrite(void* data, size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec) { doRead(); }
});
}
void processData()
{ /* Data is processed */ }
TCPSocket socket_;
propertiesPacket* properties_;
processedData* filePacket_;
short filePacketSize_;
};
class Server
{
public:
using IOContext = boost::asio::io_context;
using TCPSocket = boost::asio::ip::tcp::socket;
using TCPAcceptor = boost::asio::ip::tcp::acceptor;
Server::Server(IOContext& io, short port)
: socket_(io),
acceptor_(io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
doAccept();
}
private:
void doAccept()
{
acceptor_.async_accept(socket_,
[this](boost::system::error_code ec)
{
if (!ec) {
std::make_shared<ServerConnection>(std::move(socket_))->start();
}
doAccept();
});
}
TCPSocket socket_;
TCPAcceptor acceptor_;
};
我做错了什么?我的猜测是,在doRead 函数内部,多次调用doWrite 函数,然后该函数还调用doRead 部分是导致问题的原因,但我不知道异步写入数据的正确方法是什么多次是。但我也确信这不是我的代码中唯一表现不佳的部分。
【问题讨论】:
-
在
ServerConnection中,成员properties_和filePacketSize_永远不会被赋予除nullptr之外的其他值,因此您的程序具有UB。要么包含完整的独立代码,要么在您的代码中修复它:)。 -
如果您尝试上传(多堆)文件,请参阅例如这里:stackoverflow.com/a/66607723/85371(向下滚动到"TCP Socket Version")
-
感谢 cmets!它们的值在我遗漏的
processData函数中分配。另外,当我读取客户端发送的数据时,properties_不应该被赋值吗? -
无论如何,我会检查您评论的链接,谢谢! :)
标签: c++ networking boost ipc boost-asio