基於java nio實現的聊天室demo

 

實現的界面

客戶端

張飛用戶

劉備用戶

 

關羽用戶:

 

聊天室的具體功能如下: 

1: 羣聊

2: 顯示在線人數

3: 用戶名信息

4: 用戶離線, 通知其他在線的用戶

 

聊天室代碼的github: https://github.com/victor9309/ChatRoom

 

參考: http://ifeve.com/selectors/

代碼如下:

服務端代碼


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

public class chatServer {
    private int port;
    private Selector selector;

    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);//調整緩衝區大小爲1024字節
    private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
    private static final String USER_NAME_TAG = "$%%^&*()!@#$^%#@*()*";
    private HashSet<String> users = new HashSet<String>();
    private HashMap<String, String> Users = new HashMap<>();
    private String user_msg;

    public chatServer(int port){
        this.port = port;
    }

    public static void main(String[] args){
        new chatServer(8081).start();
    }

    public void start() {
        ServerSocketChannel ssc = null;
        try {
            ssc = ServerSocketChannel.open();
            ssc.configureBlocking(false);  //服務器配置爲非阻塞 即異步IO
            ssc.socket().bind(new InetSocketAddress(port));   //綁定本地端口
            selector = Selector.open();
            ssc.register(selector, SelectionKey.OP_ACCEPT);   //ssc註冊到selector準備連接
            System.out.println("ChatServer started ......");
        }catch (Exception e){
            e.printStackTrace();
        }

        while(true){
            try {
                int events = selector.select();
                if (events > 0) {
                    Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                    while (selectionKeys.hasNext()) {
                        SelectionKey key = selectionKeys.next();
                        selectionKeys.remove();  //移除當前的key
                        if (key.isValid()) {
                            if (key.isAcceptable()) {
                                accept(key);
                            }
                            if(key.isReadable()){
                                read(key);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void accept(SelectionKey key) throws IOException {
        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        SocketChannel clientChannel = ssc.accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("a new client connected "+clientChannel.getLocalAddress());
    }

    private void read(SelectionKey key) throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        this.readBuffer.clear();//清除緩衝區,準備接受新數據
        System.out.println("===============read");
        int numRead;
        try{
            numRead = socketChannel.read(this.readBuffer);
        }catch (IOException e){    // 客戶端斷開連接,這裏會報錯提示遠程主機強迫關閉了一個現有的連接。
            offlineUser(key);
            key.cancel();
            socketChannel.close();
            return;
        }
        user_msg = new String(readBuffer.array(),0, numRead);
        for (String s: users) System.out.println("在線用戶: " + s);

        if (user_msg.contains(USER_NAME_TAG)){   // 用戶第一次登陸, 輸入登錄名
            String user_name = user_msg.replace(USER_NAME_TAG, "");
            user_msg = "歡迎: " + user_name + " 登錄聊天室";
            users.add(socketChannel.getRemoteAddress().toString() + "===" + user_name);   // 客戶端地址和用戶名拼接在一起作爲唯一標識
            brodcast(socketChannel, user_msg);
        }
        else if (user_msg.equals("1")){       // 顯示在線人數
            user_msg = onlineUser();
            write(socketChannel, user_msg);
        }
        else {                                // 羣聊
            String user = "";
            for (String s: users) {
                if (s.contains(socketChannel.toString())){
                    String[] s1 = s.split("===");
                    if (s1.length == 2){
                        user = "用戶" + s1[1] + "對大家說:";
                    }else{
                        continue;
                    }
                }
            }
            brodcast(socketChannel, user + user_msg);
        }
    }

    private void write(SocketChannel channel, String content) throws IOException, ClosedChannelException {
        sendBuffer.clear();
        sendBuffer.put(content.getBytes());
        sendBuffer.flip();
        channel.write(sendBuffer);
        //註冊讀操作 下一次進行讀
        channel.register(selector, SelectionKey.OP_READ);
    }

    /**
     *  用戶下線,同時通知線上用戶哪些用戶下線了。
     */
    public void offlineUser(SelectionKey key) throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        for (String user: users){
            String[] s1 = user.split("===");
            if (s1.length == 2){
                String user_name = s1[1];
                if (user.contains(socketChannel.getRemoteAddress().toString())){
                    users.remove(user);
                    String message = "用戶: " + user_name + " 下線了, 拜拜";
                    brodcast(socketChannel, message);
                }
            }else{
                continue;
            }
        }
    }

    /**
     *   在線用戶
     */
    private String onlineUser(){
        String online_users = "在線用戶:\n";
        String user = "";
        for (String s: users) {
            String[] s1 = s.split("===");
            if (s1.length == 2){
                user = s1[1];
            }else{
                continue;
            }
            online_users += "\t" + user + "\n";
        }
        System.out.println(" " + online_users);
        return online_users;
    }

    /**
     *   羣聊
     */
    public void brodcast(SocketChannel except, String content) throws IOException{
        for (SelectionKey key: selector.keys()) {
            Channel targetchannel = key.channel();
            System.out.println("broadcast write:" + content);
            if(targetchannel instanceof SocketChannel && targetchannel != except) {
                SocketChannel channel = (SocketChannel) key.channel();
                write(channel, content);
            }
        }
    }
}

 

客戶端代碼


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class chatClient {
    private static final String host = "127.0.0.1";
    private static final int port = 8081;
    private  Selector selector;
    private  SocketChannel sc;
    private ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);

    private static final String USER_NAME_TAG = "$%%^&*()!@#$^%#@*()*";

    volatile boolean running = true;

    private static final Logger LOG = LoggerFactory.getLogger(chatClient.class);

    public chatClient() throws IOException{
        connect(host, port);
//         // 讀寫分離
        listen();
        
        Reader reader = new Reader();
        reader.start();
    }

    public static void main(String[] args) throws IOException{
        System.out.println("===================================================================================");
        System.out.println("輸入1: 顯示在線用戶");
        System.out.println("===================================================================================");
        new chatClient();
    }

    public void connect(String host, int port) {
        try {
            sc = SocketChannel.open();
            sc.configureBlocking(false);
            sc.connect(new InetSocketAddress(host, port));
            this.selector = Selector.open();
            sc.register( selector, SelectionKey.OP_CONNECT);   //將channel註冊到selector中
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void listen() {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            try {
                int events = selector.select();
                if (events > 0) {
                    Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                    while (selectionKeys.hasNext()) {
                        SelectionKey selectionKey = selectionKeys.next();
                        selectionKeys.remove();

                        //連接事件
                        if (selectionKey.isConnectable()) {
                            sc.finishConnect();
                            System.out.println("server connected...");
                            // 人員登錄
                            login(scanner);
                            //註冊寫操作
                            sc.register(selector, SelectionKey.OP_WRITE);
                            break;
                        }
                        else if (selectionKey.isWritable()){
                            String message = scanner.nextLine();
                            writeBuffer.clear();
                            writeBuffer.put(message.getBytes());
                            //將緩衝區各標誌復位,因爲向裏面put了數據標誌被改變要想從中讀取數據發向服務器,就要復位
                            writeBuffer.flip();
                            sc.write(writeBuffer);

                            sc.register(selector,  SelectionKey.OP_WRITE);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void login(Scanner scanner) throws IOException{
        System.out.println("請輸入登錄名: ");
        String message = scanner.nextLine();
        writeBuffer.clear();
        writeBuffer.put((USER_NAME_TAG + message).getBytes());
        //將緩衝區各標誌復位,因爲向裏面put了數據標誌被改變要想從中讀取數據發向服務器,就要復位
        writeBuffer.flip();
        sc.write(writeBuffer);
    }

    protected class Reader extends Thread {
        private final Selector writeSelector;

        Reader() throws IOException {
            this.setName("Reader");
            this.setDaemon(true);
            writeSelector = Selector.open(); // create a selector
            sc.register(writeSelector,  SelectionKey.OP_READ);
        }

        @Override
        public void run() {
            try {
                doRunLoop();
            } finally {
                LOG.info(getName() + ": stopping");
                try {
                    writeSelector.close();
                } catch (IOException ioe) {
                    LOG.error(getName() + ": couldn't close write selector", ioe);
                }
            }
        }

        private void doRunLoop() {
            while (running) {
                try {
                    int keyCt = writeSelector.select();
                    if (keyCt == 0) {
                        continue;
                    }
                    Set<SelectionKey> keys = writeSelector.selectedKeys();
                    Iterator<SelectionKey> iter = keys.iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();
                        iter.remove();
                        try {
                            if (key.isValid() && key.isReadable()) {
                                Thread.sleep(1);
                                SocketChannel client = (SocketChannel) key.channel();
                                //將緩衝區清空以備下次讀取
                                readBuffer.clear();
                                int num = client.read(readBuffer);
                                System.out.println(new String(readBuffer.array(),0, num));
                                //註冊讀操作,下一次讀
                                sc.register(selector, SelectionKey.OP_READ);
                            }
                        } catch (IOException e) {
                            LOG.debug(getName() + ": Reader", e);
                        }
                    }
                } catch (Exception e) {
                    LOG.warn(getName() + ": exception in Reader " + e);
                }
            }
        }
    }
}

 

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