java網路通訊:非同步非阻塞I/O (NIO)

來源:互聯網
上載者:User

標籤:bin   檔案   資料   block   private   退出   緩衝   exce   cte   

首先是channel,是一個雙向的全雙工系統的通道,可同時讀寫,而輸入輸出資料流都是單工的,要麼讀要麼寫。Channel分為兩大類,分別是用於網路資料的SelectableChannel和用於檔案操作的FileChannel。

注意:在java NIO庫中,所有的資料都是用緩衝區處理,常用的是ByteBuffer。

多工器Selector:

Selector會不斷輪詢註冊在其上的Channel,如果某個Channel上又新的串連接入、讀和寫事件,這個Channel就處於就緒狀態,通過SelectorKey可以擷取就緒Channel的集合。底層使用了epoll()實現,沒有最大串連控制代碼的限制。

服務端代碼:

public class TimeServer {    public static void main(String[] args) throws IOException {        int port = 8080;        MultiplexerTimeServer timeServer = new MultiplexerTimeServer(port);        new Thread(timeServer, "NIO-MultiplexerTimeServer-001").start();    }}public class MultiplexerTimeServer implements Runnable {    private Selector selector;    private ServerSocketChannel servChannel;    private volatile boolean stop;    public MultiplexerTimeServer(int port) {        try {            selector = Selector.open();            servChannel = ServerSocketChannel.open();            servChannel.configureBlocking(false);//設定非阻塞模式            servChannel.socket().bind(new InetSocketAddress(port), 1024);            servChannel.register(selector, SelectionKey.OP_ACCEPT);//將Channel註冊到selector,監聽accept事件            System.out.println("The time server is start in port : " + port);        } catch (IOException e) {            e.printStackTrace();            System.exit(1);        }    }    public void stop() {    this.stop = true;    }    @Override    public void run() {    while (!stop) {        try {            selector.select(1000);//每隔一秒輪詢一次            Set<SelectionKey> selectedKeys = selector.selectedKeys();            Iterator<SelectionKey> it = selectedKeys.iterator();            SelectionKey key = null;            while (it.hasNext()) {//如果有新的用戶端接入                key = it.next();                it.remove();                try {                    handleInput(key);//處理請求                } catch (Exception e) {                    if (key != null) {                        key.cancel();                        if (key.channel() != null)                            key.channel().close();                    }                }            }        } catch (Throwable t) {            t.printStackTrace();        }    }    // 多工器關閉後,所有註冊在上面的Channel和Pipe等資源都會被自動去註冊並關閉,所以不需要重複釋放資源    if (selector != null)        try {        selector.close();        } catch (IOException e) {        e.printStackTrace();        }    }    private void handleInput(SelectionKey key) throws IOException {        if (key.isValid()) {            // 處理新接入的請求訊息            if (key.isAcceptable()) {                // 擷取用戶端的串連通道                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();                SocketChannel sc = ssc.accept();                sc.configureBlocking(false);                //將新串連註冊到selector,並監聽讀事件                sc.register(selector, SelectionKey.OP_READ);            }            if (key.isReadable()) {                //讀取通道資料寫入位元組緩衝                SocketChannel sc = (SocketChannel) key.channel();                ByteBuffer readBuffer = ByteBuffer.allocate(1024);                int readBytes = sc.read(readBuffer);                if (readBytes > 0) {                    readBuffer.flip();                    byte[] bytes = new byte[readBuffer.remaining()];                    readBuffer.get(bytes);//位元組緩衝寫入位元組數組                    String body = new String(bytes, "UTF-8");                    System.out.println("The time server receive order : " + body);                    String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)                            ? new java.util.Date(System.currentTimeMillis()).toString()                            : "BAD ORDER";                    doWrite(sc, currentTime);//向socketchannel寫入資料                } else if (readBytes < 0) {                    // 對端鏈路關閉                    key.cancel();                    sc.close();                }             }        }    }    private void doWrite(SocketChannel channel, String response)        throws IOException {        if (response != null && response.trim().length() > 0) {            byte[] bytes = response.getBytes();            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);            writeBuffer.put(bytes);            writeBuffer.flip();            channel.write(writeBuffer);        }    }}

用戶端:

public class TimeClient {        public static void main(String[] args) {        int port = 8080;        new Thread(new TimeClientHandle("127.0.0.1", port), "TimeClient-001").start();    }}public class TimeClientHandle implements Runnable {    private String host;    private int port;    private Selector selector;    private SocketChannel socketChannel;    private volatile boolean stop;    public TimeClientHandle(String host, int port) {        this.host = host == null ? "127.0.0.1" : host;        this.port = port;        try {            selector = Selector.open();            socketChannel = SocketChannel.open();            socketChannel.configureBlocking(false);        } catch (IOException e) {            e.printStackTrace();            System.exit(1);        }    }    @Override    public void run() {        try {            doConnect();        } catch (IOException e) {            e.printStackTrace();            System.exit(1);        }        while (!stop) {            try {                selector.select(1000);                Set<SelectionKey> selectedKeys = selector.selectedKeys();                Iterator<SelectionKey> it = selectedKeys.iterator();                SelectionKey key = null;                while (it.hasNext()) {                    key = it.next();                    it.remove();                    try {                        handleInput(key);                    } catch (Exception e) {                        if (key != null) {                            key.cancel();                            if (key.channel() != null)                                key.channel().close();                        }                    }                }            } catch (Exception e) {                e.printStackTrace();                System.exit(1);            }        }        // 多工器關閉後,所有註冊在上面的Channel和Pipe等資源都會被自動去註冊並關閉,所以不需要重複釋放資源        if (selector != null)            try {                selector.close();            } catch (IOException e) {                e.printStackTrace();            }    }    private void doConnect() throws IOException {        // 如果直接連接成功,則註冊到多工器上,發送請求訊息,讀應答        if (!socketChannel.connect(new InetSocketAddress(host, port))) {            socketChannel.register(selector, SelectionKey.OP_READ);            doWrite(socketChannel);        } else//沒有直接連接成功,不代表失敗,而是說明伺服器還沒有返回TCP握手的應答訊息,所以註冊OP_CONNECT事件,監聽訊息。            socketChannel.register(selector, SelectionKey.OP_CONNECT);    }    private void handleInput(SelectionKey key) throws IOException {        if (key.isValid()) {            // 判斷是否串連成功            SocketChannel sc = (SocketChannel) key.channel();            if (key.isConnectable()) {//判斷是否有串連事件,是的話,說明未串連                if (sc.finishConnect()) {//再串連                    sc.register(selector, SelectionKey.OP_READ);                    doWrite(sc);                } else                    System.exit(1);// 串連失敗,進程退出            }            if (key.isReadable()) {                ByteBuffer readBuffer = ByteBuffer.allocate(1024);                int readBytes = sc.read(readBuffer);                if (readBytes > 0) {                    readBuffer.flip();                    byte[] bytes = new byte[readBuffer.remaining()];                    readBuffer.get(bytes);                    String body = new String(bytes, "UTF-8");                    System.out.println("Now is : " + body);                    this.stop = true;                } else if (readBytes < 0) {                    // 對端鏈路關閉                    key.cancel();                    sc.close();                }            }        }    }    private void doWrite(SocketChannel sc) throws IOException {        byte[] req = "QUERY TIME ORDER".getBytes();        ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);        writeBuffer.put(req);        writeBuffer.flip();        sc.write(writeBuffer);        if (!writeBuffer.hasRemaining())            System.out.println("Send order 2 server succeed.");    }}

 

java網路通訊:非同步非阻塞I/O (NIO)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.