Java基礎小練習-控制檯聊天室

知識儲備

  • 面向對象
  • IO
  • 異常處理
  • 多線程
  • 網絡編程

實現功能

  • 基礎羣聊功能
  • 私聊功能
  • 新加入用戶實時通知
  • 用戶控制檯關閉(用戶退出)實時通知

代碼實現

Constant.java

/**
 * 常量維護工具類
 * @author Blank
 */
public class Constant {
    private Constant() {}
    // 服務器地址
    public final static String SERVER_HOST = "localhost";
    // 服務器端口
    public final static int SERVER_PORT = 6666;
}

CloseUtil.java

import java.io.Closeable;
import java.io.IOException;

/**
 * 關閉資源工具類
 * @author Blank
 */
public class CloseUtil {
    private CloseUtil() {}

    /**
     * 關閉已打開的資源
     * @param targets
     */
    public static void close(Closeable... targets) {
        try {
            for (Closeable target : targets) {
                if (target != null) {
                    target.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

UserSend.java

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 用戶發送線程
 * @author Blank
 */
public class UserSend extends Thread {
    // 線程運行標誌
    private boolean isRunning;
    // 數據輸出流
    private DataOutputStream dos;
    // 控制檯輸入
    private BufferedReader console;

    /**
     * 構造器初始化要使用的資源
     * @param client
     */
    public UserSend(Socket client) {
        isRunning = true;
        console = new BufferedReader(new InputStreamReader(System.in));
        try {
            dos = new DataOutputStream(client.getOutputStream());
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.close(console);
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        // 運行時循環讀取消息併發送
        while (isRunning) {
            sendMessage(getMsgFromConsole());
        }
    }

    /**
     * 從控制檯讀取消息
     * @return 消息內容
     */
    private String getMsgFromConsole() {
        String messge = "";
        try {
            messge = console.readLine();
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.close(console,dos);
            e.printStackTrace();
        }
        return messge;
    }

    /**
     * 發送消息
     * @param message
     */
    private void sendMessage(String message) {
        try {
            dos.writeUTF(message);
            dos.flush();
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.close(console,dos);
            e.printStackTrace();
        }
    }
}

UserReceive.java

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 用戶接收線程
 * @author Blank
 */
public class UserReceive extends Thread {
    // 線程運行標誌
    private boolean isRunning;
    // 數據輸入流
    private DataInputStream dis;

    /**
     * 構造器初始化要使用的資源
     * @param client
     */
    public UserReceive(Socket client) {
        isRunning = true;
        try {
            dis = new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            isRunning = false;
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        // 運行時循環從服務器讀取並輸出
        while (isRunning) {
            getAndOutputMessage();
        }
    }

    /**
     * 獲取消息並輸出
     */
    private void getAndOutputMessage() {
        try {
            String message = dis.readUTF();
            System.out.println(message);
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.close(dis);
            e.printStackTrace();
        }
    }

}

User.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.Set;

public class User extends Thread {
    // 線程運行標誌
    private boolean isRunning;
    // 數據輸入流
    private DataInputStream dis;
    // 數據輸出流
    private DataOutputStream dos;

    public User(Socket client) {
        isRunning = true;
        try {
            dis = new DataInputStream(client.getInputStream());
            dos = new DataOutputStream(client.getOutputStream());
            init();
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.close(dis);
            e.printStackTrace();
        }
    }

    /**
     * 初始化 輸入用戶名
     */
    private void init() throws IOException {
        while (true) {
            dos.writeUTF("******請輸入用戶名******");
            dos.flush();
            String name = dis.readUTF().trim();
            if (name.length() == 0) {
                dos.writeUTF("******警告:用戶名不能爲空!******");
                dos.flush();
            }else {
                this.setName(name);
                dos.writeUTF( "******歡迎您! " + name + "******");
                dos.flush();
                messageTrans("用戶" + name + "加入了羣聊!",true);
                break;
            }
        }
    }

    @Override
    public void run() {
        // 線程運行時轉發消息
        while (isRunning) {
            try {
                String message = getMessage();
                if (message.length() > 0)
                    messageTrans(message,false);
            } catch (IOException e) {
                isRunning = false;
                CloseUtil.close(dis,dos);
                ChatServer.users.remove(this.getName());
                try {
                    messageTrans("用戶" + this.getName() + "退出羣聊!",true);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    /**
     * 獲取消息
     * @return
     */
    private String getMessage() throws IOException {
        String message = dis.readUTF().trim();
        return message;
    }

    /**
     * 消息轉發 約定私聊格式 @name:message
     * @param message
     * @param isSys
     * @throws IOException
     */
    private void messageTrans(String message,boolean isSys) throws IOException {
        if (isSys) {
            message = "******系統消息:" + message + "******";
            broadcast(message);
        }else {
            if (message.startsWith("@")) {
                if (message.contains(":")) {
                    // 私聊
                    int i = message.indexOf(":");
                    String name = message.substring(1,i);
                    if (name.equals(this.getName())) {
                        // 不能私聊自己
                        dos.writeUTF("******警告:不能私聊自己!******");
                        dos.flush();
                    }else {
                        message = message.substring(i+1);
                        if (ChatServer.users.containsKey(name)) {
                            User user = ChatServer.users.get(name);
                            user.dos.writeUTF(this.getName() + "悄悄對你說: " + message);
                            user.dos.flush();
                        }else {
                            // 無此用戶
                            dos.writeUTF("******警告:您私聊的用戶不存在!******");
                            dos.flush();
                        }
                    }
                }else {
                    // 私聊格式不正確
                    dos.writeUTF("******警告:您私聊的格式不正確!******");
                    dos.flush();
                }
            }else {
                // 正常羣聊
                message = this.getName() + ": " + message;
                broadcast(message);
            }
        }
    }

    /**
     * 廣播消息
     * @param message
     * @throws IOException
     */
    private void broadcast(String message) throws IOException {
        Set<Map.Entry<String, User>> users = ChatServer.users.entrySet();
        for (Map.Entry<String, User> user : users) {
            if (user.getKey().equals(this.getName())) continue;
            DataOutputStream dos = user.getValue().dos;
            dos.writeUTF(message);
            dos.flush();
        }
    }
}

ChatServer.java

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 聊天服務端
 * @author Blank
 */
public class ChatServer {
    // 用戶統一存儲
    public static Map<String,User> users = new HashMap<>();

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(Constant.SERVER_PORT);
        System.out.println("服務器已啓動!");
        while (true) {
            Socket client = server.accept();
            new Thread(()->{
                User user = new User(client);
                users.put(user.getName(),user);
                user.start();
            }).start();
        }
    }
}

ChatClient.java

import java.io.IOException;
import java.net.Socket;

/**
 * 聊天客戶端
 * @author Blank
 */
public class ChatClient {
    public static void main(String[] args) throws IOException {
        Socket client = new Socket(Constant.SERVER_HOST,Constant.SERVER_PORT);
        new UserSend(client).start();
        new UserReceive(client).start();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章