[英]Java, SocketChannel selector, combine write channel with blocking queue
我目前正在嘗試從一個線程操作一個SocketChannel(我以前實現了我想使用兩個線程和常規套接字的功能,但是每個客戶端兩個線程似乎有點多余)。 我希望能夠在有要讀取的數據時進行讀取(選擇器可以正常工作)。 我只想在阻塞隊列中有項目時寫(在我的示例中,我有幀隊列)。
@Override
public void run() {
super.run();
SelectionKey readKey = null;
try {
final int interests = SelectionKey.OP_READ;
socketChannel.configureBlocking(false);
readKey = socketChannel.register(selector, interests);
} catch (IOException e) {
e.printStackTrace();
try {
close();
} catch (Exception e1) {
throw new RuntimeException("FAILURE");
}
}
if (null != readKey) {
while (running) {
try {
System.out.println("LOOP ENTRY");
selector.select();
if (readKey.isReadable()) {
System.out.println("IS READABLE");
}
if (readKey.isWritable() && (null != framesQueues.peek())) {
System.out.println("IS WRITEABLE");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
現在,它所做的是,它不斷循環而沒有停止,這顯然很糟糕。 我正在尋找一種方法,當阻塞隊列中有一個項目或需要讀取的字節時,可以喚醒選擇器。 NIO中是否有工具可以做到這一點?
如果沒有,我該怎么辦? 我注定要為每個客戶端使用兩個線程嗎? 這是一個業余項目,但是我正在嘗試實現盡可能低的延遲,因此我不想要睡眠循環。
我當時正忙於使用nio套接字,我把一些東西放在一起,希望它很容易理解。 您需要做的就是telnet localhost 5050
。 我無權訪問其余代碼,因此我不知道您缺少什么。 我認為您雖然沒有從選擇器中清除選定的鍵,或者可能僅在完成寫入后才將興趣操作更改為(READ)。
public static void main(String... args) throws IOException {
final Selector selector = Selector.open();
//every 10 seconds this thread will go through all the connections and
//send "(x times) (date) to every client
new Thread() {
public void run() {
for (int i = 0; selector.isOpen(); i++) {
for (SelectionKey key : selector.keys()) {
if (key.channel() instanceof SocketChannel) {
((Queue<ByteBuffer>) key.attachment()).add(ByteBuffer.wrap((i + " - " + new Date() + "\n").getBytes()));
key.interestOps(OP_READ | OP_WRITE); //enable write flag
}
}
selector.wakeup(); //wakeup so it can get to work and begin writing
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
}
}
}.start();
//create server on port 5050
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.bind(new InetSocketAddress(5050));
server.register(selector, OP_ACCEPT);
//reusable buffer
final ByteBuffer readBuffer = ByteBuffer.allocate(0x1000);
while (selector.isOpen()) {
int selected = selector.select();
System.out.println("Selected " + selected + (selected == 1 ? " key." : " keys."));
if (selected > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isValid() && key.isReadable()) {
System.out.println("Readable: " + key.channel());
SocketChannel socket = ((SocketChannel) key.channel());
readBuffer.clear();
int read = socket.read(readBuffer);
if (read == -1) {
System.out.println("Socket Closed " + key.channel());
socket.close();
continue; //socket is closed. continue loop
}
//we will add what the client sent to the queue to echo it back
if (read > 0) {
readBuffer.flip();
ByteBuffer buffer = ByteBuffer.allocate(readBuffer.remaining());
((Queue<Buffer>) key.attachment()).add(buffer.put(readBuffer).flip());
key.interestOps(OP_WRITE | OP_READ); //enable write flag
}
}
if (key.isValid() && key.isWritable()) {
System.out.println("Writable: " + key.channel());
SocketChannel socket = (SocketChannel) key.channel();
//retrieve attachment(ArrayBlockingQueue<ByteBuffer>)
Queue<Buffer> dataToWrite = (Queue<Buffer>) key.attachment();
//only remove from queue once we have completely written
//this is why we call peek first, and only remove once (buffer.remaining() == 0)
for (ByteBuffer buffer; (buffer = (ByteBuffer) dataToWrite.peek()) != null;) {
socket.write(buffer);
if (buffer.remaining() == 0) dataToWrite.remove();
else break; //can not write anymore. Wait for next write event
}
//once queue is empty we need to stop watching for write events
if (dataToWrite.isEmpty()) key.interestOps(OP_READ);
}
if (key.isValid() && key.isAcceptable()) {
System.out.println("Acceptable: " + key.channel());
SocketChannel socket = ((ServerSocketChannel) key.channel()).accept();
socket.configureBlocking(false);
//add a ArrayBlockingQueue<ByteBuffer> as an attachment for the socket
socket.register(selector, OP_READ, new ArrayBlockingQueue<ByteBuffer>(1000));
}
}
selector.selectedKeys().clear(); //must clear all when finished or loop will continue selecting nothing
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.