java socketNIO demo

先啓動server 再啓動client

package learn.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
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;

public class NIOSServer {
    private int port = 8888;

    // 解碼buffer
    private Charset cs = Charset.forName("utf-8");

    /* 接受數據緩衝區 */
    private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);

    /* 發送數據緩衝區 */
    private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);

    /* 映射客戶端channel */
    private Map<String, SocketChannel> clientsMap = new HashMap<String, SocketChannel>();

    private static Selector selector;

    public NIOSServer(int port) {
        this.port = port;
        try {
            init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void init() throws IOException {
        /*
         * 啓動服務器端,配置爲非阻塞,綁定端口,註冊accept事件 ACCEPT事件:當服務端收到客戶端連接請求時,觸發該事件
         */
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(port));
        selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("server start on port:" + port);
    }

    /**
     * 服務器端輪詢監聽,select方法會一直阻塞直到有相關事件發生或超時
     */
    private void listen() {
        while (true) {
            try {
                selector.select();// 返回值爲本次觸發的事件數
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                for (SelectionKey key : selectionKeys) {
                    handle(key);
                }
                selectionKeys.clear();// 清除處理過的事件
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }

        }
    }

    /**
     * 處理不同的事件
     */
    private void handle(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel server = null;
        SocketChannel client = null;
        String receiveText = null;
        int count = 0;
        if (selectionKey.isAcceptable()) {
            /*
             * 客戶端請求連接事件 serversocket爲該客戶端建立socket連接,將此socket註冊READ事件,監聽客戶端輸入
             * READ事件:當客戶端發來數據,並已被服務器控制線程正確讀取時,觸發該事件
             */
            server = (ServerSocketChannel) selectionKey.channel();
            client = server.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            /*
             * READ事件,收到客戶端發送數據,讀取數據後繼續註冊監聽客戶端
             */
            client = (SocketChannel) selectionKey.channel();
            rBuffer.clear();
            count = client.read(rBuffer);
            if (count > 0) {
                rBuffer.flip();
                receiveText = String.valueOf(cs.decode(rBuffer).array());
                System.out.println(client.toString() + ":" + receiveText);
                dispatch(client, receiveText);
                client = (SocketChannel) selectionKey.channel();
                // client.register(selector, SelectionKey.OP_READ);
            }
        }
    }

    /**
     * 把當前客戶端信息 推送到其他客戶端
     */
    private void dispatch(SocketChannel client, String info) throws IOException {
        Socket s = client.socket();
        String name = "[" + s.getInetAddress().toString().substring(1) + ":" + Integer.toHexString(client.hashCode())
                + "]";
        if (!clientsMap.isEmpty()) {
            for (Map.Entry<String, SocketChannel> entry : clientsMap.entrySet()) {
                SocketChannel temp = entry.getValue();
                if (!client.equals(temp)) {
                    sBuffer.clear();
                    sBuffer.put((name + ":" + info).getBytes());
                    sBuffer.flip();
                    // 輸出到通道
                    temp.write(sBuffer);
                }
            }
        }
        clientsMap.put(name, client);
    }

    public static void main(String[] args) throws IOException {
        NIOSServer server = new NIOSServer(7777);
        server.listen();
    }
}

client

package learn.nio;

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.SocketChannel;
import java.util.Date;
import java.util.Scanner;
import java.util.Set;

public class NIOSClient {
    /* 發送數據緩衝區 */
    private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);

    /* 接受數據緩衝區 */
    private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);

    /* 服務器端地址 */
    private InetSocketAddress SERVER;

    private static Selector selector;

    private static SocketChannel client;

    private static String receiveText;

    private static String sendText;

    private static int count = 0;

    public NIOSClient(int port) {
        SERVER = new InetSocketAddress("localhost", port);
        init();
    }

    public void init() {
        try {
            /*
             * 客戶端向服務器端發起建立連接請求
             */
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            selector = Selector.open();
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            socketChannel.connect(SERVER);
            /*
             * 輪詢監聽客戶端上註冊事件的發生
             */
            while (true) {
                selector.select();
                Set<SelectionKey> keySet = selector.selectedKeys();
                for (final SelectionKey key : keySet) {
                    handle(key);
                }
                ;
                keySet.clear();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        new NIOSClient(7777);
    }

    private void handle(SelectionKey selectionKey) throws IOException {
        if (selectionKey.isConnectable()) {
            /*
             * 連接建立事件,已成功連接至服務器
             */
            client = (SocketChannel) selectionKey.channel();
            if (client.isConnectionPending()) {
                client.finishConnect();
                System.out.println("connect success !");
                sBuffer.clear();
                sBuffer.put((new Date() + " connected!").getBytes());
                sBuffer.flip();
                client.write(sBuffer);// 發送信息至服務器
                /*
                 * 原文來自站長網 啓動線程一直監聽客戶端輸入,有信息輸入則發往服務器端 因爲輸入流是阻塞的,所以單獨線程監聽
                 */
                new Thread() {
                    @Override
                    public void run() {
                        while (true) {
                            try {
                                sBuffer.clear();
                                Scanner cin = new Scanner(System.in);
                                sendText = cin.nextLine();
                                System.out.println(sendText);
                                /*
                                 * 未註冊WRITE事件,因爲大部分時間channel都是可以寫的
                                 */
                                sBuffer.put(sendText.getBytes("utf-8"));
                                sBuffer.flip();
                                client.write(sBuffer);
                            } catch (IOException e) {
                                e.printStackTrace();
                                break;
                            }
                        }
                    };
                }.start();
            }
            // 註冊讀事件
            client.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            /*
             * 讀事件觸發 有從服務器端發送過來的信息,讀取輸出到屏幕上後,繼續註冊讀事件 監聽服務器端發送信息
             */
            client = (SocketChannel) selectionKey.channel();
            rBuffer.clear();
            count = client.read(rBuffer);
            if (count > 0) {
                receiveText = new String(rBuffer.array(), 0, count);
                System.out.println(receiveText);
                client = (SocketChannel) selectionKey.channel();
                client.register(selector, SelectionKey.OP_READ);
            }
        }
    }
}

server通道

package learn.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
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.ArrayList;
import java.util.List;
import java.util.Set;

/*
 * 實現了nio連接,讀寫傳輸,通道關閉操作
 */
public class ServerSocketChannelDemo {

    public static boolean btemp = false;
    public static String stemp = "";
    public static Integer itemp = 0;
    public static Selector selector;
    // 裝寫入數據緩存,分配一個新的字節緩衝區
    public static ByteBuffer wbuffer = ByteBuffer.allocate(1024);
    // 裝讀入數據緩存,分配一個新的字節緩衝區
    public static ByteBuffer rbuffer = ByteBuffer.allocate(1024);
    // 裝已連接的客戶端
    public static List<SocketChannel> clients = new ArrayList<SocketChannel>();

    public static void dispose(SelectionKey key) throws IOException {
        ServerSocketChannel serverSocketChannel = null;
        SocketChannel socketChannel = null;
        btemp = key.isValid();
        if (btemp) {
            System.out.println("這事件是否有效: " + btemp);

            btemp = key.isReadable();
            if (btemp) {
                System.out.println("是否爲讀取key: " + btemp);
                // 獲取註冊該事件的通道
                socketChannel = (SocketChannel) key.channel();
                btemp = socketChannel.isConnected();
                System.out.println("是否連接成功: " + btemp);
                btemp = socketChannel.isConnectionPending();
                System.out.println("是否正在連接: " + btemp);
                btemp = socketChannel.isRegistered();
                System.out.println("是否註冊到選擇器: " + btemp);
                btemp = socketChannel.isOpen();
                System.out.println("是否打開通道: " + btemp);

                // 用之前選清空一下
                rbuffer.clear();
                itemp = socketChannel.read(rbuffer);
                // 讀取的字節數,可能爲零,如果該通道已到達流的末尾,則返回 -1 大於0表示有數據
                System.out.println("客戶端轉過來的字節長度: " + itemp);
                if (itemp > 0) {
                    // 反轉緩存
                    rbuffer.flip();
                    stemp = new String(rbuffer.array());
                    System.out.println("讀取內容: " + stemp);
                    // 轉發給其他客戶端
                    int count = 1;
                    for (SocketChannel client : clients) {
                        if (!client.equals(socketChannel)) {
                            btemp = client.isOpen();
                            System.out.println("客戶端通道是否打開: " + btemp);
                            btemp = client.isConnected();
                            System.out.println("客戶端通道連接是否成功: " + btemp);
                            btemp = client.socket().isClosed();
                            System.out.println("客戶端socket是否關閉: " + btemp);
                            System.out.println("服務端存入的客戶端數: " + clients.size());
                            if (client.isOpen() && client.isConnected()) {
                                System.out.println("轉發 " + count + " 個客戶端");
                                count++;
                                // 用之前選清空一下
                                wbuffer.clear();
                                wbuffer.put(stemp.getBytes());
                                // 裝完數據,反轉下
                                wbuffer.flip();
                                client.write(wbuffer);
                            }
                        }

                    }
                } else if (itemp == 0) {
                    System.out.println("無視");
                } else if (itemp < 0) {
                    System.out.println("客戶端已關閉,關閉該通道");
                    socketChannel.close();
                    // 註銷這無效讀事件
                    // key.cancel();
                    System.out.println("退出函數");
                    return;
                }

            }
            btemp = key.isAcceptable();
            if (btemp) {
                System.out.println("是否接受新的套接字連接key: " + btemp);
                // 獲取註冊該事件的通道
                serverSocketChannel = (ServerSocketChannel) key.channel();
                // 接收連接,返回客戶端通道
                socketChannel = serverSocketChannel.accept();

                // 將以連接的客戶端連接保存起來
                clients.add(socketChannel);
                System.out.println("打印客戶端socket地址" + socketChannel.getRemoteAddress());
                // System.out.println("處理完結束客戶端這次請求是否成功: " + btemp);
                // System.exit(1);
                // 是否阻塞
                socketChannel.configureBlocking(false);
                // 將客戶端註冊讀事件key到選擇器上
                socketChannel.register(selector, SelectionKey.OP_READ);

            }
            btemp = key.isConnectable();
            if (btemp) {
                System.out.println("是否連接操作key: " + btemp);

            }
            btemp = key.isWritable();
            if (btemp) {
                System.out.println("是否寫入key: " + btemp);
            }

        } else {
            System.out.println("有無效事件key");
        }

    }

    public static void listen() {
        while (true) {
            try {
                itemp = selector.select();
                if (itemp != 0) {
                    System.out.println("觸發事件數: " + itemp);
                }
                Set<SelectionKey> keys = selector.selectedKeys();
                if (!keys.isEmpty()) {
                    System.out.println("事件key數: " + keys.size());
                    for (SelectionKey key : keys) {
                        dispose(key);
                    }
                    // 經過事件處理方法後清空
                    keys.clear();
                }

            } catch (IOException e) {
                e.printStackTrace();
                break;
            }
        }
    }

    public static void main(String[] args) {
        try {
            // 打開通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            btemp = serverSocketChannel.isOpen();
            System.out.println("通道是否打開: " + btemp);
            // io是否阻塞
            serverSocketChannel.configureBlocking(false);
            ServerSocket serverSocket = serverSocketChannel.socket();
            // 創建通道選擇器
            selector = Selector.open();
            /*
             * 註冊選擇器 OP_ACCEPT 用於套接字接受操作的操作集位。 OP_CONNECT 用於套接字連接操作的操作集位 OP_READ
             * 用於讀取操作的操作集位 OP_WRITE 用於寫入操作的操作集位。
             * 
             */
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            btemp = serverSocketChannel.isRegistered();
            System.out.println("該通道是否有選擇器註冊: " + btemp);
            // 綁定ip和端口
            serverSocket.bind(new InetSocketAddress("localhost", 9999));

            // 如果Socket已經與一個本地端口綁定, 則返回true , 否則返回false
            btemp = serverSocket.isBound();
            System.out.println("如果Socket已經與一個本地端口綁定, 則返回true , 否則返回false: " + btemp);
            itemp = serverSocket.getLocalPort();
            System.out.println("獲取ServerSocket的port,沒設置則爲-1: " + itemp);
            serverSocketChannel = serverSocket.getChannel();// 獲取所屬nio通道
            InetAddress iaddr = serverSocket.getInetAddress();
            // 沒有設置地址則該對象爲空
            if (iaddr == null) {
                System.out.println("iaddr==null");
            } else {
                stemp = iaddr.getHostAddress();
                System.out.println("ip地址: " + stemp);
                System.out.println("InetAddress實例toString" + iaddr.toString());
            }
            SocketAddress saddr = serverSocket.getLocalSocketAddress();
            if (saddr == null) {
                System.out.println("saddr==null");
            } else {
                System.out.println("SocketAddress對象打印: " + saddr);
            }

            /*
             * ReceiveBufferSize
             * TCP發送緩存區和接收緩存區,默認是8192,一般情況下足夠了,而且就算你增加了發送緩存區,對方沒有增加它對應的接收緩衝,
             * 那麼在TCP三握手時,最後確定的最大發送窗口還是雙方最小的那個緩衝區,就算你無視,發了更多的數據,那麼多出來的數據也會被丟棄。
             * 除非雙方都協商好。
             */
            itemp = serverSocket.getReceiveBufferSize();
            System.out.println("緩存區和接收緩存區默認是8192: " + itemp);
            btemp = serverSocket.getReuseAddress();
            System.out.println(btemp);
            itemp = serverSocket.getSoTimeout();
            System.out.println("serverSocket超時設置 默認0不會超時: " + itemp);
            listen();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

client通道

package learn.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Set;

public class SocketChannelDemo {
    public static boolean btemp = false;
    public static String stemp = "";
    public static Integer itemp = 0;
    public static Selector selector = null;
    public static ByteBuffer wbuffer = ByteBuffer.allocate(1024);
    public static ByteBuffer rbuffer = ByteBuffer.allocate(1024);

    public static void dispose(SelectionKey key) throws IOException {
        SocketChannel socketChannel = null;

        btemp = key.isValid();
        if (btemp) {
            System.out.println("這事件key是否有效: " + btemp);
            btemp = key.isConnectable();
            if (btemp) {
                System.out.println("是否連接服務端成功: " + btemp);
                socketChannel = (SocketChannel) key.channel();
                // 確認連接建立完成,沒有這個服務端會一直提示有個客戶端有個連接請求沒有處理
                btemp = socketChannel.finishConnect();
                System.out.println("對服務端表示客戶端連接是否完成: " + btemp);
                System.out.println("連接服務端成功");
                if (btemp) {
                    // System.exit(1);
                    // System.out.println("退出");
                }
                // 給客戶端發數據
                stemp = new Date() + "一個客戶端連接服務端成功";
                // 用之前先清空一下
                wbuffer.clear();
                // 添加寫內容
                wbuffer.put(stemp.getBytes());
                // 反轉一下,不然服務端讀不出來
                wbuffer.flip();
                // 通過通道客戶端通道傳給服務端
                socketChannel.write(wbuffer);
                // 註冊讀事件key
                socketChannel.register(selector, SelectionKey.OP_READ);

            }

            btemp = key.isReadable();
            if (btemp) {

                System.out.println("客戶端處理讀事件");
                socketChannel = (SocketChannel) key.channel();
                btemp = socketChannel.isOpen();
                System.out.println("客戶端通道是否打開: " + btemp);
                btemp = socketChannel.isConnected();
                System.out.println("客戶端通道是否連接成功: " + btemp);
                if (!socketChannel.isOpen() || !socketChannel.isConnected()) {
                    System.out.println("該通道以關閉");

                }
                // 用之前先清空一下
                rbuffer.clear();
                itemp = socketChannel.read(rbuffer);
                if (itemp > 0) {
                    // 裝完數據反轉一下
                    rbuffer.flip();
                    stemp = new String(rbuffer.array());
                    System.out.println("服務器傳過來的消息" + stemp);
                    btemp = socketChannel.socket().isClosed();
                    System.out.println("客戶端socket是否關閉: " + btemp);

                } else if (itemp == 0) {

                } else if (itemp < 0) {
                    System.out.println("服務端關閉了通道");
                    socketChannel.close();
                }

                if (socketChannel.isOpen()) {
                    // 接收服務端的消息退出
                    socketChannel.close();
                    // key.cancel();
                    System.out.println("客戶端關閉通道,關閉客戶端程序");
                    System.exit(1);
                }
            }
        } else {
            System.out.println("有無效事件key");
        }

    }

    public static void listen() throws IOException {
        while (true) {
            itemp = selector.select();
            if (itemp != 0) {
                System.out.println("客戶端事件數: " + itemp);
                itemp = 0;
            }
            Set<SelectionKey> keys = selector.selectedKeys();
            if (!keys.isEmpty()) {
                System.out.println("事件key數: " + keys.size());
                for (SelectionKey key : keys) {
                    dispose(key);
                }
                // 經過事件處理方法後清空
                keys.clear();
            }

        }
    }

    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        // 是否阻塞
        socketChannel.configureBlocking(false);
        selector = Selector.open();
        socketChannel.register(selector, SelectionKey.OP_CONNECT);
        // 綁定客戶端網絡地址,不綁定則隨機分配
        // socketChannel.bind(new InetSocketAddress("localhost", 6666));
        // 連接服務端網絡地址
        socketChannel.connect(new InetSocketAddress("localhost", 9999));

        // 返回相關socket
        Socket socket = socketChannel.socket();
        // 獲取所屬nio通道
        socket.getChannel();

        btemp = socket.getKeepAlive();
        System.out.println("是否爲保留socket: " + btemp);
        InetAddress iaddr = socket.getLocalAddress();
        if (iaddr == null) {
            System.err.println("iaddr==null");
        } else {
            stemp = iaddr.getHostAddress();
            System.out.println("獲取InetAddress實例hostAddress 默認 0.0.0.0: " + stemp);
            stemp = iaddr.getHostName();
            System.out.println("獲取InetAddress實例hostName 默認 0.0.0.0: " + stemp);
        }
        itemp = socket.getLocalPort();
        System.out.println("socket端口,沒設置爲-1: " + itemp);
        SocketAddress socketAddress = socket.getRemoteSocketAddress();
        System.out.println("socketAddress實例打印沒設置爲null: " + socketAddress);
        itemp = socket.getSendBufferSize();
        System.out.println("tcp發送緩存區: " + itemp);
        itemp = socket.getReceiveBufferSize();
        System.out.println("tcp接收緩存區: " + itemp);
        btemp = socket.isBound();
        System.out.println("是否綁定網絡地址" + btemp);
        btemp = socket.isClosed();
        System.out.println("是否以關閉" + btemp);
        btemp = socket.isConnected();
        System.out.println("是否在連接" + btemp);
        btemp = socket.isInputShutdown();
        System.out.println("是否關閉輸入流" + btemp);
        btemp = socket.isOutputShutdown();
        System.out.println("是否關閉輸出流" + btemp);
        listen();
    }

}
發佈了49 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章