我有一个使用Java NIO使用TCP套接字连接到C++服务器的Java客户机。这适用于Linux,AIX和HP/UX,但在Solaris下,OP_CONNECT
事件不会触发。Java Solaris NIO OP_CONNECT问题
进一步的细节:
Selector.select()
将返回0,而 '选择的密钥集' 是空的。- 只有在连接到本地计算机(通过回送或以太网接口)时才会出现此问题,但连接到远程计算机时会发生此问题。
- 我已经在两个不同的Solaris 10计算机下确认了该问题;物理SPARC和使用JDK 1.6.0_21和_26版本的虚拟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>
凡口应该是7,如果你对正在运行的真正的回声服务;即:
java -cp . NioTest3 127.0.0.1:7 5
如果你不能得到真实回波服务运行,那么源头之一是here。在Solaris下编译回声服务器:
$ cc -o echoserver echoserver.c -lsocket -lnsl
像这样运行:
$ ./echoserver 8007 > out 2>&1 &
这已被报告给孙为bug。
是的,一旦打开了频道并在“选择器”中注册了该频道,并且在调用connect之后设置了对OP_CONNECT的兴趣(否则引发ConnectionPendingException)。阻塞连接会导致我的问题 - 连接发生在特定的时间,如果它干扰其他线程的I/O,那就很糟糕。 – trojanfoe
@trojanfoe该通道需要注册并在connect()之前设置为OP_CONNECT,否则可能会错过该事件。在你调用connect()之前,不能抛出ConnectPendingException * - 我不明白。这个例外的原因在Javadoc中是非常明确的,但事实并非如此。 – EJP
我已经做了这个改变,并在Linux下进行了测试(它的工作原理),但是它并没有解决Solaris下的问题。我提到了错误的异常 - 它是NoConnectionPendingException,但不再发生,所以请忽略它。 – trojanfoe