NIO Server

package com.shengsiyuan.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

public class NioServer {

    private static Map<String, SocketChannel> clientMap = new HashMap();

    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//建立一個ServerSocketChannel
        serverSocketChannel.configureBlocking(false);//非阻塞
        ServerSocket serverSocket = serverSocketChannel.socket();//經過服務端的channel獲取服務端的socket,
        serverSocket.bind(new InetSocketAddress(8899));

        Selector selector = Selector.open();//服務端channel註冊到選擇器上,
        //選擇器能夠關聯多個channel對象,這裏只有一個channel。
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//如今關注服務端channel的鏈接事件,

        while (true) {
            try {//異常try()catch{}
                selector.select();//阻塞,等着關注的事件發生,返回發生的關注事件的數量。

                Set<SelectionKey> selectionKeys = selector.selectedKeys();//一個個的事件,如今只有一個OP_ACCEPT事件,

                selectionKeys.forEach(selectionKey -> {
                    final SocketChannel client;

                    try {
                        //OP_ACCEPT事件
                        if (selectionKey.isAcceptable()) {
                            //這個selectionKey關聯的channel是服務端channel,OP_ACCEPT是服務端channel關注的事件(並註冊在選擇器上)
                            //因此這裏能夠強制轉爲服務端channel,
                            ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
                            client = server.accept();//接受鏈接,真正接收鏈接以後返回SocketChannel對象,就是與客戶端通訊的socket,
                            //對於當前通道,服務端socket就用不上了,用SocketChannel
                            client.configureBlocking(false);//非阻塞的
                            //轉而把SocketChannel註冊到選擇器,並關注讀事件,
                            client.register(selector, SelectionKey.OP_READ);
                            //此時這個選擇器有2個socket,一個服務端channel一個SocketChannel,一個關注鏈接一個關注數據讀取,
                            String key = "" + UUID.randomUUID().toString() + "";

                            clientMap.put(key, client);

                        //OP_READ事件
                        } else if (selectionKey.isReadable()) {
                            client = (SocketChannel) selectionKey.channel();//確定是客戶端關聯的socket
                            ByteBuffer readBuffer = ByteBuffer.allocate(1024);

                            int count = client.read(readBuffer);//讀到buffer

                            if (count > 0) {
                                readBuffer.flip();

                                Charset charset = Charset.forName("utf-8");//編碼
                                //發過來的數據轉成string
                                String receivedMessage = String.valueOf(charset.decode(readBuffer).array());
                                //打印客戶端發過來的數據
                                System.out.println(client + ": " + receivedMessage);

                                String senderKey = null;

                                for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                                    if (client == entry.getValue()) {
                                        senderKey = entry.getKey();
                                        break;
                                    }
                                }

                                for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                                    SocketChannel value = entry.getValue();
                                    //先寫到ByteBuffer
                                    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                                    writeBuffer.put((senderKey + ": " + receivedMessage).getBytes());

                                    writeBuffer.flip();
                                    //而後buteBuffer寫出去到channel
                                    value.write(writeBuffer);
                                }
                            }
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                });

                selectionKeys.clear();//事件處理完成以後要清空,不然下次還要處理,就會報空指針。就是iter.remove();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}
package com.shengsiyuan.nio;

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.time.LocalDateTime;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NioClient {

    public static void main(String[] args) throws IOException {
        try {
            SocketChannel socketChannel = SocketChannel.open();//@587
            socketChannel.configureBlocking(false);

            Selector selector = Selector.open();
            //OP_ACCEPT是接受鏈接,OP_CONNECT是發起鏈接,
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            socketChannel.connect(new InetSocketAddress("127.0.0.1", 8899));

            while (true) {
                selector.select();//阻塞
                Set<SelectionKey> keySet = selector.selectedKeys();

                for (SelectionKey selectionKey : keySet) {
                    //已經創建好了鏈接
                    if (selectionKey.isConnectable()) {
                        SocketChannel client = (SocketChannel) selectionKey.channel();//就是以前的那個socket @587
                        //鏈接是否處於進行狀態
                        if (client.isConnectionPending()) {
                            client.finishConnect();//完成鏈接,如今鏈接真正創建好了,
                            //向服務器發送鏈接創建好了的消息
                            ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                            writeBuffer.put((LocalDateTime.now() + " 鏈接成功").getBytes());
                            //翻轉
                            writeBuffer.flip();
                            //寫到channel
                            client.write(writeBuffer);

                            ExecutorService executorService = Executors.newSingleThreadExecutor(
                                    Executors.defaultThreadFactory());//只有一個線程的線程池
                            executorService.submit(() -> {
                                while (true) {
                                    try {
                                        writeBuffer.clear();
                                        InputStreamReader input = new InputStreamReader(System.in);
                                        BufferedReader br = new BufferedReader(input);
                                        String sendMessage = br.readLine();
                                        //寫入到buffer
                                        writeBuffer.put(sendMessage.getBytes());
                                        //翻轉
                                        writeBuffer.flip();
                                        //buffer寫出去
                                        client.write(writeBuffer);
                                    } catch (Exception ex) {
                                        ex.printStackTrace();
                                    }
                                }
                            });
                        }
                        //給這個channel註冊讀取事件,
                        client.register(selector, SelectionKey.OP_READ);//@587
                    } else if (selectionKey.isReadable()) {
                        SocketChannel client = (SocketChannel) selectionKey.channel();//@587
                        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        int count = client.read(readBuffer);
                        if (count > 0) {
                            String receivedMessage = new String(readBuffer.array(), 0, count);//字節數組轉字符串
                            System.out.println(receivedMessage);
                        }
                    }
                }
                keySet.clear();//清除事件,就是清除SelectionKey集合。
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
相關文章
相關標籤/搜索