提问者:小点点

Asio同步IPv6 UDP服务器


https://github.com/thinkalvb/rtds-server我正在制作一个简单的UDP IPv6服务器,它打印远程endpoint发送的UDP数据包的版本。 但是这个代码的行为很奇怪。 当发送IPv6和IPv4数据包时,它将打印IPv6。 我做错了什么? [在Win10中使用Packet Sender Portable 6.2.3(127.0.0.1和::1)进行测试]

#include <asio.hpp>
#include <iostream>
#include "udp_peer.h"
#include <thread>


asio::io_context ioContext;
asio::io_context::work worker(ioContext);

void runServer()
{
    ioContext.run();
}

int main()
{
    asio::ip::udp::endpoint mUDPep(asio::ip::udp::v6(), 321);
    asio::ip::udp::socket mUDPsock(ioContext);
    std::thread thread(runServer);
    thread.detach();

    asio::error_code ec;
    UDPpeer udpPeer(&mUDPsock);  // Ignore this, it contains the character array
    asio::ip::udp::endpoint ep;


    mUDPsock.open(mUDPep.protocol(), ec);
    mUDPsock.bind(mUDPep, ec);
    while (true)
    {
        auto dataSize = mUDPsock.receive_from(udpPeer.getReadBuffer(), ep);
        if (ep.address().is_v4())
            std::cout << "IPv4";
        else
            std::cout << "IPv6";
    }
}

共1个答案

匿名用户

你只听V6。

eP并不规定您如何接收。

您的v6端点能够同时接收这两种信息。 打印实际终结点以查看:

#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::udp;

int main() {
    asio::thread_pool context(1);
    udp::socket sock(context, {udp::v6(), 8888});

    udp::endpoint ep;
    char arr[4096];

    while (true) {
        /*auto n =*/ sock.receive_from(asio::buffer(arr), ep);
        std::cout 
            << std::boolalpha << ep.address().is_v4() << " "
            << ep << "\n";
    }

    context.join();
}

现在发送两个数据包:

echo -n "hello world $RANDOM" | nc -6 -w 0 -u ::1 8888
echo -n "hello world $RANDOM" | nc -4 -w 0 -u 127.0.0.1 8888

打印:

false [::1]:49972
false [::ffff:127.0.0.1]:34368

作为比较,使用udp::socket sock(context,{udp::v4(),8888});并不接收v6数据包:

true 127.0.0.1:39805

换句话说,因为您的套接字绑定到v6,所以您得到的地址被映射成好像用:

if (a.is_v4())
    return asio::ip::address_v6::v4_mapped(a.to_v4());

检查v6是否与v4映射或兼容:

asio::ip::address_v4 a4;
if (a6.is_v4_compatible() || a6.is_v4_mapped())
    a4 = a6.to_v4();

看起来更现代的界面应该是这样的

 a4 = make_address_v4(asio::ip::v4_mapped, a6);
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::udp;

int main() {
    asio::thread_pool context(1);
    udp::socket sock(context, {udp::v6(), 8888});

    udp::endpoint ep;
    char arr[4096];

    for (auto n=2; n--;) {
        /*auto n =*/ sock.receive_from(asio::buffer(arr), ep);
        asio::ip::address_v4 a4;

        {
            auto a6 = ep.address().to_v6();
            if (a6.is_v4_compatible() || a6.is_v4_mapped())
                a4 = a6.to_v4();
        }

        std::cout
            << (a4.is_unspecified()? "not-mapped" : a4.to_string()) << " "
            << ep << "\n";
    }

    context.join();
}

打印

127.0.0.1 [::ffff:127.0.0.1]:54859
not-mapped [::1]:36231