在 SocketChannel 上使用选择器
Using Selector on SocketChannel
我正尝试在多个套接字通道上等待数据读取。我从网上复制了示例代码,但它对我不起作用。问题是 "select" 调用正确阻塞,直到收到第一个数据,但此后立即 returns 0(未准备好)。我的代码(针对单个频道)如下。
final String HOST = "<REDACTED>.com";
final int PORT = 2000;
SocketChannel MyChannel;
Selector MySelector;
void doSelect()
{
SelectionKey key;
int nready;
long nr;
try {
ByteBuffer bytebuf = ByteBuffer.allocate( 1024 ); // allocate bytebuffer
InetSocketAddress addr = new InetSocketAddress( HOST, PORT); // form addr
MyChannel = SocketChannel.open(); // create socketchan
MyChannel.connect( addr ); // connect to data src
while( !MyChannel.finishConnect() ) Thread.sleep( 1000 ); // wait til connected
MyChannel.configureBlocking( false ); // set non-blocking
MySelector = Selector.open(); // open new selector
key = MyChannel.register( MySelector, SelectionKey.OP_READ ); // register for reads
while( true ) { // loop forever
nready = MySelector.select(); // block til something ready
if( nready <= 0 ) { // nothing ready?
logit( "not ready: select returned "+nready );
MyChannel.close();
break; // give up
}
if( key.isReadable() ) { // readable?
nr = MyChannel.read( bytebuf ); // read data
logit( "read "+nr+" bytes" );
}
} // end while
} // end try
catch( Exception e ) {
logit( "doSelect: "+e );
return;
}
}
//@ Log a message to logcat
void logit(String msg) { Log.i( "MYAPP", msg ); }
2019 年 10 月 19 日更新
根据@user207421 的建议,我修改了代码以迭代 selected 键。这没有区别。 select() 调用在第一次接收后 仍未阻塞 并且从通道读取的数据显示已读取 0 个字节..
新代码摘录(为了(我的)可读性而重命名的一些变量)
while( true ) { // loop forever
nready = selector.select(); // call select: block til something ready
if( nready <= 0 ) { // nothing ready? (should never happen)
break; // give up
}
// PROCESS ALL SELECTED KEYS
Set<SelectionKey> selected = selector.selectedKeys(); // get selected key set
Iterator<SelectionKey> iter = selected.iterator(); // create iterator
while( iter.hasNext() ) { // while has more
key = iter.next(); // get next key
if( key.isReadable() ) { // check if ready to read
nr = chan.read( bb ); // read from channel
if( nr > 0 ) { // got some data?
printByteBuffer( bb );
}
else {
// should never happen! (but it does)
}
}
else if( key.isWritable()) {} // never happens!
else if( key.isAcceptable()) {} // never happens!
else if( key.isConnectable()) {} // never happens!
iter.remove();
} // end while hasnext
} // end while
//@ Convert ByteBuffer to byte array and print
void printByteBuffer(
ByteBuffer bb ) // BB to print
{
int nrem;
byte[] bytes;
String str;
bb.flip(); // reset position
nrem = bb.remaining();
bytes = new byte[nrem]; // alloc byte array
bb.get( bytes ); // get bytes from bytebuf
str = new String( bytes ); // cnvt to string
log( "received \""+str+"\"" );
}
非常感谢任何建议。
我根据评论找到了解决方案。显然 ByteBuffer 是 "full"。我通过在通道 read() 之前发出一个“.clear()”来解决这个问题。
我不明白为什么这是必要的,因为 ByteBuffer 比任何接收到的数据都大(1000 字节),但尽管如此,它仍然有效。
回复:阻塞模式:Every 我在网上找到的例子说通道应该设置为非阻塞,选择器 select() 才能正常工作。无论如何,我曾短暂地尝试过阻止模式,但对此并不满意。
我正尝试在多个套接字通道上等待数据读取。我从网上复制了示例代码,但它对我不起作用。问题是 "select" 调用正确阻塞,直到收到第一个数据,但此后立即 returns 0(未准备好)。我的代码(针对单个频道)如下。
final String HOST = "<REDACTED>.com";
final int PORT = 2000;
SocketChannel MyChannel;
Selector MySelector;
void doSelect()
{
SelectionKey key;
int nready;
long nr;
try {
ByteBuffer bytebuf = ByteBuffer.allocate( 1024 ); // allocate bytebuffer
InetSocketAddress addr = new InetSocketAddress( HOST, PORT); // form addr
MyChannel = SocketChannel.open(); // create socketchan
MyChannel.connect( addr ); // connect to data src
while( !MyChannel.finishConnect() ) Thread.sleep( 1000 ); // wait til connected
MyChannel.configureBlocking( false ); // set non-blocking
MySelector = Selector.open(); // open new selector
key = MyChannel.register( MySelector, SelectionKey.OP_READ ); // register for reads
while( true ) { // loop forever
nready = MySelector.select(); // block til something ready
if( nready <= 0 ) { // nothing ready?
logit( "not ready: select returned "+nready );
MyChannel.close();
break; // give up
}
if( key.isReadable() ) { // readable?
nr = MyChannel.read( bytebuf ); // read data
logit( "read "+nr+" bytes" );
}
} // end while
} // end try
catch( Exception e ) {
logit( "doSelect: "+e );
return;
}
}
//@ Log a message to logcat
void logit(String msg) { Log.i( "MYAPP", msg ); }
2019 年 10 月 19 日更新
根据@user207421 的建议,我修改了代码以迭代 selected 键。这没有区别。 select() 调用在第一次接收后 仍未阻塞 并且从通道读取的数据显示已读取 0 个字节..
新代码摘录(为了(我的)可读性而重命名的一些变量)
while( true ) { // loop forever
nready = selector.select(); // call select: block til something ready
if( nready <= 0 ) { // nothing ready? (should never happen)
break; // give up
}
// PROCESS ALL SELECTED KEYS
Set<SelectionKey> selected = selector.selectedKeys(); // get selected key set
Iterator<SelectionKey> iter = selected.iterator(); // create iterator
while( iter.hasNext() ) { // while has more
key = iter.next(); // get next key
if( key.isReadable() ) { // check if ready to read
nr = chan.read( bb ); // read from channel
if( nr > 0 ) { // got some data?
printByteBuffer( bb );
}
else {
// should never happen! (but it does)
}
}
else if( key.isWritable()) {} // never happens!
else if( key.isAcceptable()) {} // never happens!
else if( key.isConnectable()) {} // never happens!
iter.remove();
} // end while hasnext
} // end while
//@ Convert ByteBuffer to byte array and print
void printByteBuffer(
ByteBuffer bb ) // BB to print
{
int nrem;
byte[] bytes;
String str;
bb.flip(); // reset position
nrem = bb.remaining();
bytes = new byte[nrem]; // alloc byte array
bb.get( bytes ); // get bytes from bytebuf
str = new String( bytes ); // cnvt to string
log( "received \""+str+"\"" );
}
非常感谢任何建议。
我根据评论找到了解决方案。显然 ByteBuffer 是 "full"。我通过在通道 read() 之前发出一个“.clear()”来解决这个问题。
我不明白为什么这是必要的,因为 ByteBuffer 比任何接收到的数据都大(1000 字节),但尽管如此,它仍然有效。
回复:阻塞模式:Every 我在网上找到的例子说通道应该设置为非阻塞,选择器 select() 才能正常工作。无论如何,我曾短暂地尝试过阻止模式,但对此并不满意。