[英]Simple Boost::Asio asynchronous UDP echo server
我目前正在閱讀一本名為“C++ Crash Course”的關於 C++ 的書。 關於網絡的章節展示了如何使用 Boost::Asio 編寫一個簡單的大寫 TCP 服務器(同步或異步)。 其中一個練習是用 UDP 重新創建它,這就是我遇到的問題。 這是我的實現:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/algorithm/string/case_conv.hpp>
using namespace boost::asio;
struct UdpServer {
explicit UdpServer(ip::udp::socket socket)
: socket_(std::move(socket)) {
read();
}
private:
void read() {
socket_.async_receive_from(dynamic_buffer(message_),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec || this->message_ == "\n") return;
boost::algorithm::to_upper(message_);
this->write();
}
);
}
void write() {
socket_.async_send_to(buffer(message_),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec) return;
this->message_.clear();
this->read();
}
);
}
ip::udp::socket socket_;
ip::udp::endpoint remote_endpoint_;
std::string message_;
};
int main() {
try {
io_context io_context;
ip::udp::socket socket(io_context, ip::udp::v4(), 1895);
UdpServer server(std::move(socket));
io_context.run();
} catch (std::exception & e) {
std::cerr << e.what() << std::endl;
}
}
(注意:原始示例使用enable_shared_from_this
this
shared_ptr
捕獲到 lambdas 中,但我故意省略了它以查看沒有它會發生什么。)
我的代碼無法編譯,我覺得完全解析錯誤消息需要一千年的時間(因為它很大,所以發布在 pastebin.com 上)。
似乎問題在於緩沖區的使用/構造方式錯誤,但我不知道這段代碼到底有什么問題。 這里關於 Asio 的幾個答案要么使用 TCP 要么解決一個完全不同的問題,所以我犯的錯誤必須是非常基本的。 我在 Asio 文檔中沒有找到任何相關內容。
公平地說,Asio 對我的新手來說似乎太復雜了。 可能我現在還沒有資格使用它。 盡管如此,我仍然希望完成練習並繼續前進。 任何幫助,將不勝感激。
模板具有最難看的編譯器錯誤消息。 您通常只需要通過編譯器錯誤 output 來 go 並在您自己的源文件中查找第一個引用。 翼:
/home/atmaks/Code/CCC_chapter20/main.cpp:53:9: required from here
無論如何,在 Visual Studio 上,錯誤更加明顯。 (不是真的,它只是更好地識別了違規行)。
盯着它,思考你一生中所有的決定,這些決定讓你首先想要在 C++ 中進行開發。 :)
我一生都無法弄清楚如何讓dynamic_buffer
工作。 可能只是async_read
不喜歡這種類型。 而且我認為這實際上對 UDP 有意義。 接收緩沖區必須在同步模式下的recvfrom
調用之前調整大小。 而且我懷疑異步 UDP,尤其是對於 Windows,緩沖區必須向下傳遞給 kernel 才能被填滿。 到那時再調整大小已經太晚了。
Asio 缺乏適當的文檔,給我們留下了難以理解的模板類型。 唯一值得的 Asio 文檔是體面的示例——其中沒有一個引用dynamic_buffer
。
所以讓我們改成一個固定大小的緩沖區來接收。
當我們使用它時,它不喜歡您的套接字構造函數並引發了異常。 所以我把它修好了,這樣它就可以工作了。
#include <iostream>
#include <boost/asio.hpp>
#include <boost/algorithm/string/case_conv.hpp>
using namespace boost::asio;
struct UdpServer {
explicit UdpServer(ip::udp::socket socket)
: socket_(std::move(socket)) {
read();
}
private:
void read() {
socket_.async_receive_from(buffer(data_, 1500),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec)
{
return;
}
data_[length] = '\0';
if (strcmp(data_, "\n") == 0)
{
return;
}
boost::algorithm::to_upper(data_);
this->write();
}
);
}
void write() {
socket_.async_send_to(buffer(data_, strlen(data_)),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec) return;
data_[0] = '\0';
this->read();
}
);
}
ip::udp::socket socket_;
ip::udp::endpoint remote_endpoint_;
char data_[1500 + 1]; // +1 for we can always null terminate safely
};
int main() {
try {
io_context io_context;
ip::udp::endpoint ep(ip::udp::v6(), 1895); // also listens on ipv4
ip::udp::socket sock(io_context, ep);
UdpServer server(std::move(sock));
io_context.run();
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
更新我確實讓 dynamic_buffer 工作,但它仍然需要進行預分配。
更新 read() function 的開頭如下:
void read() {
auto db = dynamic_buffer(message_);
auto b = db.prepare(1500);
socket_.async_receive_from(b,
...
這至少可以讓您堅持使用std::string
而不是使用平面 C 數組。
現在證明它有效:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.