DatagramChannel SocketException "Invalid argument"

DatagramChannel SocketException "Invalid argument"

我的代码在 DatagramChannel 中调用 send() 时抛出 SocketException。 javadoc 甚至没有提到这是可能的。我在某处发现 post 建议使用“-Djava.net.preferIPv4Stack=true”来使用 IPv4 堆栈,但这没有帮助。我只想发送一个非阻塞 DNS 请求。

Exception in thread "main" java.net.SocketException: Invalid argument
    at sun.nio.ch.DatagramChannelImpl.send0(Native Method)
    at sun.nio.ch.DatagramChannelImpl.sendFromNativeBuffer(DatagramChannelImpl.java:536)
    at sun.nio.ch.DatagramChannelImpl.send(DatagramChannelImpl.java:513)
    at sun.nio.ch.DatagramChannelImpl.send(DatagramChannelImpl.java:477)
    at Test.main(Test.java:31)

这是相关代码:

public class Test {

    private static final byte[] request = hexStringToByteArray("674B010000010000000000000377777706676F6F676C6503636F6D0000010001");

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                 + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }

    public static void main(final String[] args) throws IOException {

        String ip = InetAddress.getLocalHost().getHostAddress();
        int port = 1024 + (new Random()).nextInt(64507);

        DatagramChannel channel = DatagramChannel.open();
        channel.configureBlocking(false);
        channel.socket().bind(new InetSocketAddress(ip, port));
        int sent = channel.send(ByteBuffer.wrap(request), new InetSocketAddress(InetAddress.getByName("8.8.8.8"), 53));

    }
}

编辑:因为这里无法重现一些版本:

jurgen@home:~/workspace/Resolver/bin$ java -version
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
jurgen@home:~/workspace/Resolver/bin$ uname -a
Linux home 3.13.0-44-generic #73-Ubuntu SMP Tue Dec 16 00:22:43 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
jurgen@home:~/workspace/Resolver/bin$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.1 LTS
Release:    14.04
Codename:   trusty
jurgen@home:~/workspace/Resolver/bin$ java Test
Exception in thread "main" java.net.SocketException: Invalid argument
    at sun.nio.ch.DatagramChannelImpl.send0(Native Method)
    at sun.nio.ch.DatagramChannelImpl.sendFromNativeBuffer(DatagramChannelImpl.java:536)
    at sun.nio.ch.DatagramChannelImpl.send(DatagramChannelImpl.java:513)
    at sun.nio.ch.DatagramChannelImpl.send(DatagramChannelImpl.java:477)
    at Test.main(Test.java:31)

我认为您的问题是您通过调用 InetAddress.getLocalHost() 将客户端的本地套接字显式绑定到 localhost。而不是做:

channel.bind(new InetSocketAddress(ip, port));

您应该在不指定 IP 部分 的情况下实例化 InetSocketAddress 。然后系统会将套接字绑定到所有可用接口(0.0.0.0):

channel.bind(new InetSocketAddress(port));

如果想显式也可以这样调用:

channel.bind(new InetSocketAddress("*", port));

这解决了我的问题。基本原理是客户端套接字必须绑定到 传出接口 而不是环回。