Boost::Asio 点对点 udp 聊天

Posted

技术标签:

【中文标题】Boost::Asio 点对点 udp 聊天【英文标题】:Boost::Asio peer-to-peer udp chat 【发布时间】:2014-07-15 12:22:39 【问题描述】:

我正在编写用于交换短信的点对点(它不应该有服务器 - 这是一项任务)程序。这是一个非常小的聊天。只是消息,没有别的。这是我第一次使用 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】:

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

通常这种任务是通过使用多播来完成的。广播会在网络上造成过多的负载。

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

#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;

完整的源代码可用here。

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。 这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review @Tomerikoo 完成。现在够了吗?

以上是关于Boost::Asio 点对点 udp 聊天的主要内容,如果未能解决你的问题,请参考以下文章

java 通过TCPUDP 协议实现多人聊天,点对点,文件传送-----分服务器端和客户端

Swoole+Redis+webSocket实现点对点即时聊天

Swoole+Redis+webSocket实现点对点即时聊天

asmack android点对点聊天还在聊天中显示第三条用户聊天消息

Activemq mqtt 点对点聊天实现(转载)

Android Socket UDP 点对点,或者广播通讯,包含发送端和接收端