gpt4 book ai didi

java - Java Solaris NIO OP_CONNECT问题

转载 作者:搜寻专家 更新时间:2023-10-31 19:30:46 25 4
gpt4 key购买 nike

我有一个Java客户端,该客户端使用使用Java NIO的TCP套接字连接到C++服务器。这在Linux,AIX和HP/UX上有效,但是在Solaris上,OP_CONNECT事件从不触发。

更多详细信息:

  • Selector.select()返回0,并且“所选键集”为空。
  • 该问题仅在连接到本地计算机(通过环回或以太网接口(interface))时发生,但在连接到远程计算机时起作用。
  • 我已经在两台不同的Solaris 10计算机上确认了该问题;使用JDK版本1.6.0_21和_26的物理SPARC和虚拟x64(VMWare)。

  • 这是一些演示此问题的测试代码:
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;

    public class NioTest3
    {
    public static void main(String[] args)
    {
    int i, tcount = 1, open = 0;
    String[] addr = args[0].split(":");
    int port = Integer.parseInt(addr[1]);
    if (args.length == 2)
    tcount = Integer.parseInt(args[1]);
    InetSocketAddress inetaddr = new InetSocketAddress(addr[0], port);
    try
    {
    Selector selector = Selector.open();
    SocketChannel channel;
    for (i = 0; i < tcount; i++)
    {
    channel = SocketChannel.open();
    channel.configureBlocking(false);
    channel.register(selector, SelectionKey.OP_CONNECT);
    channel.connect(inetaddr);
    }
    open = tcount;
    while (open > 0)
    {
    int selected = selector.select();
    System.out.println("Selected=" + selected);
    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
    while (it.hasNext())
    {
    SelectionKey key = it.next();
    it.remove();
    channel = (SocketChannel)key.channel();
    if (key.isConnectable())
    {
    System.out.println("isConnectable");
    if (channel.finishConnect())
    {
    System.out.println(formatAddr(channel) + " connected");
    key.interestOps(SelectionKey.OP_WRITE);
    }
    }
    else if (key.isWritable())
    {
    System.out.println(formatAddr(channel) + " isWritable");
    String message = formatAddr(channel) + " the quick brown fox jumps over the lazy dog";
    ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
    channel.write(buffer);
    key.interestOps(SelectionKey.OP_READ);
    }
    else if (key.isReadable())
    {
    System.out.println(formatAddr(channel) + " isReadable");
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    channel.read(buffer);
    buffer.flip();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
    String message = new String(bytes);
    System.out.println(formatAddr(channel) + " read: '" + message + "'");
    channel.close();
    open--;
    }
    }
    }

    }
    catch (IOException e)
    {
    e.printStackTrace();
    }
    }

    static String formatAddr(SocketChannel channel)
    {
    return Integer.toString(channel.socket().getLocalPort());
    }
    }

    您可以使用命令行运行此命令:
    java -cp . NioTest3 <ipaddr>:<port> <num-connections>

    如果您要使用真正的回显服务,则port应该为7; IE。:
    java -cp . NioTest3 127.0.0.1:7 5

    如果您无法运行真正的回显服务,则来源为 here。使用以下命令在Solaris下编译回显服务器:
    $ cc -o echoserver echoserver.c -lsocket -lnsl

    并像这样运行它:
    $ ./echoserver 8007 > out 2>&1 &

    据报道,这是Sun的 bug

    最佳答案

    您的错误报告已关闭为“不是错误”,并附有解释。您将忽略connect()的结果,如果为true,则表示OP_CONNECT将永远不会触发,因为该 channel 已连接。如果返回的结果为false,则只需要整个OP_CONNECT/finishConnect() megillah。因此,除非OP_CONNECT返回false,否则您甚至不应该注册connect(),更不用说在甚至调用connect().之前注册它了。
    进一步说明:
    在后台,OP_CONNECTOP_WRITE是同一件事,这解释了其中的一部分。
    由于您只有一个线程,解决方法是在阻塞模式下进行连接,然后将I/O切换为非阻塞。
    在使用Selector注册 channel 之后,您是否正在执行select()?
    处理非阻塞连接的正确方法如下:

    channel.configureBlocking(false);
    if (!channel.connect(...))
    {
    channel.register(sel, SelectionKey.OP_CONNECT, ...); // ... is the attachment, or absent
    }
    // else channel is connected, maybe register for OP_READ ...
    // select() loop runs ...
    // Process the ready keys ...
    if (key.isConnectable())
    {
    if (channel.finishConnect())
    {
    key.interestOps(0); // or SelectionKey.OP_READ or OP_WRITE, whatever is appropriate
    }
    }
    在查看您的扩展代码后,一些非详尽的注释:
  • 关闭 channel 会取消 key 。您不需要两者都做。
  • 错误地实现了非静态removeInterest()方法。
  • TYPE_DEREGISTER_OBJECT也关闭 channel 。不确定这是否是您真正想要的。我以为它应该只是取消键,并且应该有一个单独的操作来关闭 channel 。
  • 在小型方法和异常处理方面,您已经过头了。 addInterest()removeInterest()是很好的例子。他们捕获了异常,将其记录下来,然后进行设置或清除了一点,就好像未发生异常一样进行处理:一行代码。而且最重要的是,它们同时具有静态和非静态版本。所有调用key.cancel()channel.close()等的小方法也是如此。这没有任何意义,只是增加了代码行数。它只会增加模糊性,并使您的代码更难以理解。只需内联进行所需的操作,并在select循环的底部具有单个捕获器即可。
  • 如果finishConnect()返回false,则不是连接失败,它尚未完成。如果抛出Exception,则表明连接失败。
  • 您正在同时注册OP_CONNECTOP_READ。这没有道理,可能会引起问题。在OP_CONNECT触发之前,没有任何内容可供阅读。只需先注册OP_CONNECT
  • 您正在为每次读取分配一个ByteBuffer。这非常浪费。在连接的整个生命周期中,请使用相同的连接器。
  • 您将忽略read()的结果。可以为零。它可以是-1,表示EOS,您必须在该 channel 上关闭 channel 。您还假设一次读取即可获得完整的应用程序消息。你不能以为。这就是为什么在连接的整个生命周期内都应使用单个ByteBuffer的另一个原因。
  • 您将忽略write()的结果。调用时它可能小于buffer.remaining()。可以为零。
  • 通过将NetSelectable设置为键附件,可以大大简化此过程。然后,您可以删除一些内容,例如 channel 映射和断言,因为键的 channel 必须始终等于键的附件的 channel 。
  • 我也肯定会将finishConnect()代码移动到NetSelector,并让connectEvent()只是成功/失败通知。您不想散布这种东西。对readEvent()进行相同的操作,即使用NetSelector提供的缓冲区在NetSelectable中进行读取,然后将读取结果通知给NetSelectable:count或-1或
    异常(exception)。写入时同上:如果 channel 可写,请从NetSelectable中写入一些内容,将其写入NetSelector中,然后通知结果。您可以让通知回调返回一些内容以指示下一步要执行的操作,例如关闭 channel 。

  • 但这实际上是所需复杂度的五倍,事实证明您确实有此错误。简化你的头。

    关于java - Java Solaris NIO OP_CONNECT问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6540346/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com