Boost::Asio点对点UDP聊天

4
我正在编写一个点对点程序(不应该有服务器-这是一个任务),用于交换文本消息。它是一个非常简单的聊天程序,只有消息,没有其他内容。这是我第一次使用Boost :: Asio练习,因此我有一些问题。
正如我所说,我的聊天应该是点对点的,并且应该使用UDP协议。我认为,最好的方法是使用广播。第一个问题是:我怎样才能了解新连接?
另一个问题在于发送消息:我将其发送到广播地址,然后它会传播到本地网络中的所有计算机。这样做正确吗?
这段代码发送消息并接收其回复。就像回声一样。这是正确的吗?
#include <iostream>
#include <boost/asio.hpp>
#include <boost/array.hpp>

int main()
{
    try 
    {
        namespace ip = boost::asio::ip;
        boost::asio::io_service io_service;

        ip::udp::socket socket(io_service,
            ip::udp::endpoint(ip::udp::v4(), 1555));
        socket.set_option(boost::asio::socket_base::broadcast(true));

        ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 1555);

        boost::array<char, 4> buffer1;
        socket.send_to(boost::asio::buffer(buffer1), broadcast_endpoint);

        ip::udp::endpoint sender_endpoint;

        boost::array<char, 4> buffer2;
        std::size_t bytes_transferred = 
            socket.receive_from(boost::asio::buffer(buffer2), sender_endpoint);

        std::cout << "got " << bytes_transferred << " bytes." << std::endl;
    }
    catch (std::exception &e)
    {
        std::cerr << e.what();
    }

    system("PAUSE");

    return 0;
}
1个回答

1

在Ubuntu 20.04.3 LTS和Boost.Asio 1.71上进行测试。

通常使用组播来完成此类任务。广播会在网络上产生过多的负载。

基于发送方接收方示例,同时结合两者,您应该在多播地址上打开套接字,这代表一个“聊天室”,同时订阅该多播组以接收其他聊天参与者发送的消息。

#include <iostream>
#include <string>

#include <boost/asio.hpp>

constexpr std::uint16_t multicast_port = 30001;

class Peer {
public:
    Peer(boost::asio::io_context& io_context,
         const boost::asio::ip::address& chat_room,
         const std::string& nickname)
        : socket_(io_context)
        , multicast_endpoint_(chat_room, multicast_port)
        , nickname_(nickname)
{

    boost::asio::ip::udp::endpoint listen_endpoint(chat_room, multicast_port);
    socket_.open(listen_endpoint.protocol());
    socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
    socket_.bind(listen_endpoint);

请注意我们使用了reuse_address选项,这样你可以在本地测试这个例子。
如果你想接收发送到多播组的消息,你必须订阅该多播组:
socket_.set_option(boost::asio::ip::multicast::join_group(chat_room));

正如您所问,如果您想了解新连接(尽管UDP是一种无连接协议),您可以发送多播欢迎消息:

auto welcome_message = std::string(nickname_ + " connected to the chat\n");

        socket_.async_send_to(boost::asio::buffer(welcome_message), multicast_endpoint_,
                              [this](const boost::system::error_code& error_code, std::size_t bytes_sent){
            if (!error_code.failed()){
                std::cout << "Entered chat room successfully" << std::endl;
            }
        });

所以,现在我们需要建立两个循环: 第一个循环将期望本地用户的输入,将其发送到多播组,并等待另一个用户的输入,而另一个循环将侦听套接字上传入的UDP数据报,并在每次接收到数据报时打印数据报内容,然后返回到套接字侦听状态。
void do_receive(){
        socket_.async_receive_from(boost::asio::buffer(receiving_buffer_), remote_endpoint_,
                              [this](const boost::system::error_code& error_code, std::size_t bytes_received){
            if (!error_code.failed() && bytes_received > 0){
                auto received_message_string = std::string(receiving_buffer_.begin(), receiving_buffer_.begin() + bytes_received);
                // We don't want to receive the messages we produce
                if (received_message_string.find(name_) != 0){
                    std::cout.write(receiving_buffer_.data(), bytes_received);
                    std::cout << std::flush;
                }
                do_receive();
            }
        });
    }
void do_send(){
    std::string nickname = nickname_;
    std::string message;
    std::getline(std::cin, message);
    std::string buffer = name.append(": " + message);
    socket_.async_send_to(boost::asio::buffer(buffer, maximum_message_size_), multicast_endpoint_,
                       [this, message](const boost::system::error_code& /*error_code*/, std::size_t bytes_sent){
        std::cout << "You: " << message << std::endl;
        do_send();
    });
}

我们在每个完成处理程序中调用相同的IO函数,以实现循环效果,看起来仍然像递归。

目前,我们需要做的就是在单独的线程中发布每个函数调用,因为io_context.run()调用会阻塞,否则其中一个循环将阻塞另一个循环,因此我们在每个线程中调用io_context.run():

int main(int argc, char* argv[])
    {
    boost::asio::thread_pool thread_pool(2);
    if(argc != 3){
        std::cerr << "Usage: ./peer <your_nickname> <multicast_address>" << std::endl;
        std::exit(1);
    }

    boost::asio::io_context io_context;
    boost::asio::ip::address chat_room(boost::asio::ip::make_address(argv[2]));
    Peer peer(io_context, chat_room, argv[1]);

    boost::asio::post(thread_pool, [&]{
        peer.do_receive();
        io_context.run();
    });
    boost::asio::post(thread_pool, [&]{
        peer.do_send();
        io_context.run();
    });
    thread_pool.join();

    return 0;

}

完整的源代码可以在这里找到。

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community
这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - Tomerikoo
@Tomerikoo 完成了。现在够好了吗? - Andrey Burmagin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接