Java NIO SocketChannel说没有数据时,有(或选择器没有通知我)
我有一个功能正常的客户机-服务器设备,它可以使用NIO成功地相互连接和发送消息 现在我唯一的困惑是,当socketChannel.read()返回零时,我应该如何继续阅读 我有一个协议,发送前4个字节作为预期的传入字节数。即使有这么多钱,我也遇到了一个潜在的问题 但是,对于测试,有时我可能会阅读以下内容:Java NIO SocketChannel说没有数据时,有(或选择器没有通知我),java,sockets,tcp,nio,socketchannel,Java,Sockets,Tcp,Nio,Socketchannel,我有一个功能正常的客户机-服务器设备,它可以使用NIO成功地相互连接和发送消息 现在我唯一的困惑是,当socketChannel.read()返回零时,我应该如何继续阅读 我有一个协议,发送前4个字节作为预期的传入字节数。即使有这么多钱,我也遇到了一个潜在的问题 但是,对于测试,有时我可能会阅读以下内容: 5 // Read 5 bytes when calling socketChannel.read() 0 // Read 0 bytes when calling socketChannel
5 // Read 5 bytes when calling socketChannel.read()
0 // Read 0 bytes when calling socketChannel.read() immediately after
5 // socketChannel.read()
0 // socketChannel.read()
7 // socketChannel.read() (Done since I have all my bytes)
0
0
0
...
当我到达零点时,我假设我已经完成了阅读,需要等待更多的数据
但是,当我执行此操作时,当我稍后再次执行selectNow()时,OP_READ似乎不会被触发。我检查了这个键,它的readyops()和interestTops()设置为1(这是OP_READ),但它不想识别是时候再次读取了
我发现如果我继续循环阅读,我可能会得到如下结果:
5 // Read 5 bytes when calling socketChannel.read()
0 // Read 0 bytes when calling socketChannel.read() immediately after
5 // socketChannel.read()
0 // socketChannel.read()
7 // socketChannel.read() (Done since I have all my bytes)
0
0
0
...
我在这里感到困惑,因为这意味着:
- 那里没有数据,因此可用的0是合法的,但是当其他数据进入时,选择器拒绝返回带有selectNow()的键
- 数据都在那里,但由于某种原因,读取时返回0
我是不是应该继续调查他们?这让我对OP_READ何时实际启动感到困惑。这是由于我的一个错误,我没有对读取的bytebuffer调用
.clear()
。这会导致即使数据已流入,也返回0读取
这个例子对于那些想要了解简单客户机如何工作(尽管异常处理非常糟糕)的人也很有用。无法保证这将正常工作,并且可能会有问题,因为它被设计为一个快速且肮脏的测试
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class Test {
public static final int PORT = 22222;
public static void main(String[] args) throws IOException {
Thread s = new Thread(new Server());
Thread c = new Thread(new Client());
s.start();
c.start();
}
}
class Client implements Runnable {
public Selector selector;
public SocketChannel sc;
public Client() throws IOException {
selector = Selector.open();
}
@Override
public void run() {
try {
sc = SocketChannel.open();
sc.socket().setTcpNoDelay(true);
sc.configureBlocking(false);
SelectionKey k = sc.register(selector, SelectionKey.OP_CONNECT);
boolean firstConnect = sc.connect(new InetSocketAddress("localhost", Test.PORT));
if (firstConnect) {
System.out.println("Connected on first connect, de-registering OP_CONNECT");
k.interestOps(SelectionKey.OP_READ);
}
while (true) {
int keys = selector.selectNow();
if (keys > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isConnectable()) {
boolean finishConnectResult = sc.finishConnect();
key.interestOps(SelectionKey.OP_READ);
System.out.println("Finished connection: " + finishConnectResult);
}
if (key.isReadable()) {
ByteBuffer bb = ByteBuffer.allocate(2);
int bytesRead = 0;
while ((bytesRead = sc.read(bb)) > 0) {
bb.flip();
System.out.println(bytesRead + " bytes read");
System.out.println(bb.get() + ", " + bb.get());
//bb.clear(); // If this is not commented, it will not be handled properly.
}
System.out.println("Last bytes read value = " + bytesRead);
System.exit(0);
}
}
}
Thread.sleep(5);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
class Server implements Runnable {
public Selector selector;
public SocketChannel sc;
public Server() throws IOException {
selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(Test.PORT));
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
@Override
public void run() {
boolean notSentData = true;
try {
while (true) {
int keys = selector.selectNow();
if (keys > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
sc = ssc.accept();
if (sc != null) {
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true); // Required in my application
sc.register(selector, SelectionKey.OP_WRITE);
System.out.println("Server accepted connection");
} else {
System.out.println("Got null connection");
}
}
}
}
if (sc != null && notSentData) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.put(new byte[]{ 1, 2, 3, -1});
bb.flip();
int wrote = sc.write(bb);
System.out.println("Wrote " + wrote + " bytes");
notSentData = false;
}
Thread.sleep(5);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
很好奇。你当然不必重新注册。发布一些代码。@EJP没有调用
bb.clear()是我的错代码>在读取之间。我将使用一个工作示例更新代码,并对其原因进行注释。您应该调用compact()
,而不是clear()。
否则您将丢失数据。每次只能从缓冲区中取出两个字节:此代码将丢弃其余的字节。