1. 程式人生 > >SelectionKey,read,

SelectionKey,read,

SelectionKey:
- 每次一個Channel註冊到一個Selector時,都會返回一個SelectionKey的例項,
- 在使用一個SelectionKey例項之前,我們可以通過isValid()來判斷這個例項的合法性(有沒有被其他執行緒取消,對應channel有沒有被關閉,對應的selector有沒有被關閉,等等)
- 一個SelectionKey包含有兩個被操作的集,一個是interest set(決定了selector呼叫select()方法時對它對應Channel的哪一種操作進行測試(一共有四種可以選擇)),另一個是ready set(用於標識這個key是否處於準備狀態),這兩個集都是通過整數來標識的
- 針對第三點中提到的“四種選擇”,不是對每一個Channel都有效的,這還取決於這個Channel的型別,具體的還可以通過SelectableChannel提供的validOps()方法,來查詢那些set是被當前這個Channel所支援的
- SelectionKey可以有一個attachmen(附件),這個附件作為一個物件可以攜帶一些其他的東西。通過attach方法可以新增這個附件
- SelectionKey是執行緒安全的,這意味它可以用於多併發程式設計之中,因為它會被某個正在操作它的Selector強制同步

常用方法分析
- public final boolean isAcceptable();//測試key對應的channel是否準備建立一個新的socket connection,如果一個key對應的channel不支援socket-accept操作,通常會返回false
- public final boolean isConnectable();//測試key對應的channel是否已經完成或者失敗了socket-connection(socket建立)操作,也就是說,要讓這個方法返回true,他必須滿足兩個條件,第一,key呼叫的readyOps()函式的返回值必須是true,即呼叫這個方法之後返回的int數字,必須是SelectionKey的Operation-set當中的一種,第二,這個key的Operation-set bit 必須是OP_CONNECTION(對應的是一個數字),
- public final boolean isAcceptable();//測試這個key所對應的channel是否已經準備好來建立一個新的socket連線,這個方法需要返回true,也需要滿足兩個條件,,第一,key呼叫的readyOps()函式的返回值必須是true,即呼叫這個方法之後返回的int數字,必須是SelectionKey的Operation-set當中的一種,第二,這個key的Operation-set bit 必須是OP_ACCEPT(對應的是一個數字),
- 關乎上面兩個方法的兩個條件,第一個條件代表的是這個key對應的channel準備好了,後一個條件是在準備好了的基礎之上,滿足特定的某個操作。
- 類似的還有 public final boolean isReadable(),public final boolean isWritable()

下面給出測試程式碼:


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;
import java.util.Iterator;
import
java.util.Set; public class NIOServer { public static void main(String[] args) { // TODO Auto-generated method stub String host="127.0.0.1"; int port = 8090; new Thread(new NIOServerHandler(host,port)).start(); } } class NIOServerHandler implements Runnable { private ServerSocketChannel ssc; private Selector selector; public NIOServerHandler(String host,int port) { // TODO Auto-generated constructor stub try { ssc=ServerSocketChannel.open(); ssc.socket().bind(new InetSocketAddress(host,port)); ssc.configureBlocking(false); selector=Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { // TODO Auto-generated method stub while(true){ try { selector.select(3000); Set<SelectionKey> sk=selector.selectedKeys(); Iterator<SelectionKey> it=sk.iterator(); while(it.hasNext()){ SelectionKey key=it.next(); it.remove(); handlerKey(key); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void handlerKey(SelectionKey key) throws IOException { // TODO Auto-generated method stub if(key.isValid()){ //判斷是是否有接入請求, if(key.isAcceptable()){ SocketChannel sc=((ServerSocketChannel)key.channel()).accept(); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); } //判斷是否有等待讀取的訊息 if(key.isReadable()){ ByteBuffer readBuffer=ByteBuffer.allocate(1024); ((SocketChannel)key.channel()).read(readBuffer); readBuffer.flip(); byte[] bytes=new byte[readBuffer.remaining()]; readBuffer.get(bytes); System.out.println("收到新訊息"+new String(bytes,"UTF-8")); //返回一個結果 byte[] writeBytes ="我收到了一條訊息".getBytes("UTF-8"); ByteBuffer writeBuffer = ByteBuffer.allocate(writeBytes.length); writeBuffer.put(writeBytes); writeBuffer.flip(); ((SocketChannel)key.channel()).write(writeBuffer); } } } }
package nettyJava.Chapter2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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.Iterator;
import java.util.Set;

public class NIOClient {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String host="127.0.0.1";
        int port = 8090;
        new Thread(new NIOClientHandler(host,port)).start();
    }

}


class NIOClientHandler implements Runnable{

    private SocketChannel sc;
    private String host;
    int port;
    private Selector selector;

    public NIOClientHandler(String host,int port) {
        // TODO Auto-generated constructor stub
        this.host=host;
        this.port=port;
        try {
            sc=SocketChannel.open();
            sc.configureBlocking(false);
            selector =Selector.open();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            socketChannelConnect();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        while(true){
            try {
                selector.select(6000);
                //獲取被選擇key,並且以此檢查並操作每個key;
                Set<SelectionKey> selectedKey=selector.selectedKeys();
                Iterator<SelectionKey> it=selectedKey.iterator();
                while(it.hasNext()){
                    SelectionKey key=it.next();
                    it.remove();
                    handlerKey(key);
                }
            } catch (IOException e) {

                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }


    private void handlerKey(SelectionKey key) throws IOException{
        if(key.isValid()){
            SocketChannel socket=(SocketChannel) key.channel();
            //判斷socket是否處於連線成功,如果成功則需要註冊
            if(key.isConnectable()){

                if(socket.finishConnect()){//表明連線已經建立
                    /*
                     * Finishes the process of connecting a socket channel.
                     * true if, and only if, this channel's socket is now connected
                     */

                    socket.register(selector, SelectionKey.OP_READ);//註冊這個channel,並且說明感興趣的方面
                }else{
                    System.exit(1);
                }
            }

            //判斷是否可讀

            if(key.isReadable()){
                doRead(key);
                doWrite(socket);
            }
        }
    }

    private void doRead(SelectionKey key) throws IOException{
        System.out.println("從server輸入的資料");
        ByteBuffer readBuffer=ByteBuffer.allocate(1024);
        SocketChannel sc=(SocketChannel) key.channel();
        int readSize=sc.read(readBuffer);
        readBuffer.flip();
        if(readSize>0){
            byte[] bytes=new byte[readBuffer.remaining()];
            readBuffer.get(bytes);
            System.out.println(new String(bytes,"UTF-8"));
        }
    }

    private void socketChannelConnect() throws IOException{
        //將這個sc連線到server,並且註冊到監聽器
        sc.connect(new InetSocketAddress(host,port));
        if(sc.finishConnect()){
            sc.register(selector, SelectionKey.OP_READ);
            doWrite(sc);
        }
        else
            sc.register(selector, SelectionKey.OP_CONNECT);
    }

    private void doWrite(SocketChannel socket) throws IOException{
        //byte[] bytes="this is a test".getBytes("UTF-8");

        System.out.println("向server輸出資料:回車後ok結束");
        BufferedReader consoleIn=new BufferedReader(new InputStreamReader(System.in));
        String sendMessage="";
        while(true){
            String temp=consoleIn.readLine();
            if(temp.equals("ok"))break;
            sendMessage+=temp+"\n";
        }
        byte[] sendBytes=sendMessage.getBytes("UTF-8");
        ByteBuffer writeBuffer=ByteBuffer.allocate(sendBytes.length);
        writeBuffer.put(sendBytes);
        writeBuffer.flip();
        socket.write(writeBuffer);
    }

}

伺服器端的每一個和客戶端通訊的socket,都是通過serverSocket的accept方法來建立的,