我有一個使用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