簡體   English   中英

Simple Boost::Asio 異步 UDP 回顯服務器

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM