首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我在绑定/连接地址匹配的情况下间歇性地接收PortUnreachableException?

为什么我在绑定/连接地址匹配的情况下间歇性地接收PortUnreachableException?
EN

Stack Overflow用户
提问于 2021-06-18 15:35:42
回答 1查看 573关注 0票数 0

我有一个小的概念证明,我试图通过另一个端口路由UDP流量,而任何一方都不知道端口的转换。如果我禁用connect()调用,只需执行“触发和忘记”数据报,我就可以让它正常工作。此外,即使使用connect()调用,我的代码也能工作,但它似乎会选择特定的流量并抛出一个PortUnreachableException。

实际的服务器/客户端(我正在路由的流量)可能向两个方向发送流量。其中一些流量通过的很好,但有些没有,并导致无法到达的端口错误。我已经能够重现这种有问题的服务器/客户端行为,并且我已经使用一个名为run_buggySend()的简单方法将该实现放入我下面的run_buggySend代码中,您将在下面看到这个方法。

我向后和横向地查看了代码,不知道为什么会抛出异常。我知道大多数人都使用bind()或connect(),但两者都不使用,但我的用例同时要求两者,因为我正在路由的一些协议需要非常具体的端口,用于两个方向的通信。此外,虽然我可以摆脱connect()调用以使其工作,但我喜欢缓存路由信息以提高性能的想法,也不明白为什么它不应该兼容。然后,如果没有bind(),来自硬编码端口的流量永远不会到达我的DatagramChannel。

我还想知道这是否会碰到一些错误的Java代码,因为端口无法访问的异常甚至不会从更相关/更正确的DatagramChannel抛出。如果我将触发数据报发送到3000,那么13001通道将抛出错误。同样,如果我将其发送到13001,则3000通道将抛出一个错误。

也就是说,我将通过下面的最小复制代码。感谢您的任何提示或帮助!

代码语言:javascript
复制
import java.io.IOException;
import java.math.BigInteger;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.ByteChannel;
import java.nio.charset.StandardCharsets;

public class ByteChannelUDPBridge implements Runnable
{
    private final ByteBuffer buff = ByteBuffer.allocateDirect(4096);

    private final ByteChannelUDP channelA;
    private final ByteChannelUDP channelB;

    private final int portBase = 3000;
    private final int portOffset = 10000;

    public ByteChannelUDPBridge() throws IOException
    {
        InetAddress loopback = InetAddress.getLoopbackAddress();

        channelA = new ByteChannelUDP(
            new InetSocketAddress(loopback, portBase),                      //    bind=3000
            new InetSocketAddress(loopback, portBase + 1));                 // connect=3001
        channelB = new ByteChannelUDP(
            new InetSocketAddress(loopback, portOffset + portBase + 1),     //    bind=13001
            new InetSocketAddress(loopback, portOffset + portBase));        // connect=13000
    }

    public void transfer(ByteChannel chanIn, ByteChannel chanOut) throws IOException
    {
        try
        {
            chanIn.read(buff);

            if (buff.position() > 0)
            {
                System.out.print(chanIn + " recieved " + buff.position() + " bytes: " + buff.position() + " bytes: ");

                buff.flip();
                logToHex(StandardCharsets.UTF_8.decode(buff));
                buff.rewind();
                chanOut.write(buff);
                buff.clear();
            }
        }
        catch (PortUnreachableException ex)
        {
            System.err.println(chanIn + ": " + ex);
        }
    }

    public void run()
    {
        try
        {
            while (true)
            {
                transfer(channelA, channelB); // 3000  >> B:13001 -> C:13000
                transfer(channelB, channelA); // 13001 >> B:3000  -> C:3001
                Thread.sleep(1);
            }
        }
        catch (IOException | InterruptedException ex) { ex.printStackTrace(); }
        finally
        {
            try
            {
                channelA.close();
                channelB.close();
            }
            catch (IOException dontCare) { dontCare.printStackTrace(); }
        }
    }

    public void run_buggySend()
    {
        try
        {
            DatagramSocket sock = new DatagramSocket(3001); // 13000
            InetSocketAddress recip = new InetSocketAddress(InetAddress.getLoopbackAddress(), 3000); // 13001
            byte[] buffer = new byte[]{1, 2, 3};

            while (true)
            {
                sock.send(new DatagramPacket(buffer, buffer.length, recip));
                Thread.sleep(15000);
            }
        }
        catch (IOException | InterruptedException ex)
        {
            ex.printStackTrace();
        }
    }

    public void logToHex(CharBuffer arg) { System.out.format("%x\n", new BigInteger(1, arg.toString().getBytes())); }

    public static void main(String[] args) throws Exception
    {
        ByteChannelUDPBridge listener = new ByteChannelUDPBridge();
        Thread t1 = new Thread(listener);
        t1.start();

        Thread t2 = new Thread(listener::run_buggySend);
        t2.start();
    }
}

代码语言:javascript
复制
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.DatagramChannel;

public class ByteChannelUDP implements ByteChannel
{
    private final DatagramChannel channel;

    private final SocketAddress bind, connect;

    public ByteChannelUDP(InetSocketAddress addrBind, InetSocketAddress addrConnect) throws IOException
    {
        bind = addrBind;
        connect = addrConnect;
        channel = DatagramChannel.open();
        channel.configureBlocking(false);
        channel.bind(bind);
        channel.connect(connect);
    }

    @Override
    public int read(ByteBuffer dst) throws IOException
    {
        //return channel.read(dst); // this also causes the unreachable exception
        int before = dst.remaining();
        SocketAddress rec = channel.receive(dst);
        return dst.remaining() - before;
    }

    @Override
    public int write(ByteBuffer src) throws IOException
    {
        //return channel.write(src); // not used unless i can get read() to work
        return channel.send(src, connect);
    }

    @Override
    public boolean isOpen() { return channel.isOpen(); }

    @Override
    public void close() throws IOException { channel.close(); }

    @Override
    public String toString()
    {
        return bind.toString() + " <> " + connect.toString();
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-18 20:00:34

偶然发现了问题和解决方案。当recieve() / read()方法抛出PortUnreachableException时,触发错误的实际上是send() / write()

其中一方的接收器显然配置不正确,也不容易修复,因此可以理解的是,中间写将出现错误。

然而,由于一些难以理解的原因,Java决定提醒调用者注意read()方法中无法交付的数据报更有意义,尽管失败的是write()。此外,这种行为似乎也没有被记录下来。Java应该将异常抛出到下一次写入或单独的“错误轮询”方法中,而不是从与写入操作的失败几乎完全无关的读取操作中抛出异常。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68037907

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档