天天看點

解決NIO服務端select一直為-1 && 服務端一直有可讀事件

  在進行NIO程式設計的時候,發現用戶端隻往服務端發送了一條消息後,就下線了,但是服務端select方法的傳回值總是1,出現了死循環,經過調試發現,Selectionkeys方法傳回的總是進行讀操作的SocketChannel,也就是說注冊的SocketChannel一直有讀的就緒事件,可是用戶端已經關閉了。總結來說就是:用戶端主動關閉連接配接,服務端會一直出現可讀事件,read一直傳回-1,在網上查找了大量的部落格後,解決了這個問題。

  解決的方案是服務端主動關閉通道SocketChannel,當關閉後,select就不會再繼續關注該通道,這樣select就不會總是傳回1了

  很明顯,可以解決問題,但是如果我的用戶端沒有下線,而是想繼續發消息怎麼辦?這就要依靠read的傳回值來進行判斷了,如果read的傳回值是-1,則主動關閉通道

while ((read = socketChannel.read(buffer)) > 0) {
                            System.out.println("read:"+read);
                            buffer.flip();
                            System.out.println(socketChannel.getRemoteAddress()+":"+new String(buffer.array()));
                        }
                        if(read == -1) {
                            System.out.println("read:"+read);
                            socketChannel.close();
                        }
           

最後附上測試的服務端和用戶端源碼:

package selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 *
 */
public class Server {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.打開通道
        ServerSocketChannel server = ServerSocketChannel.open();
        //2.綁定端口
        server.socket().bind(new InetSocketAddress(8888));
        //3.得到選擇器
        Selector selector = Selector.open();
        //4.設定非阻塞,并注冊到選擇器
        server.configureBlocking(false);
        server.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            int select = selector.select();
            System.out.println("select:"+select);
            Thread.sleep(500);
            if(select > 0) {
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    if(next.isAcceptable()) {
                        System.out.println("accpet");
                        ServerSocketChannel serverSocketChannel = ((ServerSocketChannel) next.channel());
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                    } else if(next.isReadable()) {
                        System.out.println("read");
                        SocketChannel socketChannel = ((SocketChannel) next.channel());
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        buffer.clear();
                        int read = 0;
                        while ((read = socketChannel.read(buffer)) > 0) {
                            System.out.println("read:"+read);
                            buffer.flip();
                            System.out.println(socketChannel.getRemoteAddress()+":"+new String(buffer.array()));
                        }
                        if(read == -1) {
                            System.out.println("read:"+read);
                            socketChannel.close();
                        }
                        buffer.clear();
                    }
                    iterator.remove();
                    System.out.println("開始下次select");
                }
            } else {
                System.out.println("去幹其他事情");
            }
        }
    }
}
           
package selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 *
 */
public class Client {
    public static void main(String[] args) throws IOException, InterruptedException {
        //拿到通道
        SocketChannel client = SocketChannel.open();
        //2.設定非阻塞模式
        client.configureBlocking(false);
        //3.連接配接伺服器
        client.connect(new InetSocketAddress("127.0.0.1", 8888));

        //
        while (!client.finishConnect()) {
            System.out.println("建立連接配接中...");
        }
        ByteBuffer buffer = ByteBuffer.allocate(100);
        String msg = "this is one";
        buffer.put(msg.getBytes());
        buffer.flip();
        client.write(buffer);
        buffer.clear();


        //測試用戶端發兩次資料
        Thread.sleep(5000);
        msg = "this is two";
        buffer.put(msg.getBytes());
        buffer.flip();
        client.write(buffer);
        buffer.clear();
        Thread.sleep(10000);
        client.close();
    }
}
           

繼續閱讀