首頁 > 軟體

Java用區域網實現聊天室功能

2022-05-19 19:00:50

本文範例為大家分享了Java用區域網實現聊天室的具體程式碼,供大家參考,具體內容如下

類和介面

Server類(伺服器端)

package Test;

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

public class Server {
    private static final int SERVER_PORT=30000;
    //使用CrazyitMap物件來儲存每個客戶名字和對應輸出流之間的對應關係
    public static CrazyitMap<String,PrintStream> clients=new CrazyitMap<>();
    public void init(){
        try( //建立監聽的ServerSocket
             ServerSocket ss=new ServerSocket(SERVER_PORT))
        {
            //採用死迴圈來不斷地接收來自使用者端的請求
            while(true){
                Socket socket=ss.accept();
                new ServerThread(socket).start();
            }
        }
        //如果丟擲異常
        catch (IOException ex){
            System.out.println("伺服器啟動失敗,是否埠"+SERVER_PORT+"已被佔用");
        }
    }
    public static void main(String[] args){
        Server server=new Server();
        server.init();
    }
}

ServerThread類

package Test;

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

public class ServerThread extends Thread {
    private Socket socket;
    BufferedReader br = null;
    PrintStream ps = null;

    //定義一個構造器,用於接收一個Socket來建立ServerThread執行緒
    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        try {
            //獲取該Socket對應的輸入流
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //獲取該Socket對應的輸出流
            ps = new PrintStream(socket.getOutputStream());
            String line = null;
            while ((line = br.readLine()) != null) {
                //如果讀到的行以CrazyitProtocol.USER_ROUND開始,並以其結束
                //則可以確定讀到的是使用者登入的使用者名稱
                if (line.startsWith(CrazyitProtocol.USER_ROUND) && line.endsWith(CrazyitProtocol.USER_ROUND)) {
                    //得到真實訊息
                    String userName = getRealMsg(line);
                    //如果使用者名稱重複
                    if (Server.clients.map.containsKey(userName)) {
                        System.out.println("重複");
                        ps.println(CrazyitProtocol.NAME_REP);
                    } else {
                        System.out.println("成功");
                        ps.println(CrazyitProtocol.LOGIN_SUCCESS);
                        Server.clients.put(userName, ps);
                    }
                }
                //如果讀到的行以CrazyitProtocol.PRIVATE_ROUND開始,
                //則可以確定是私聊資訊,私聊資訊只向特定的輸入流傳送
                else if (line.startsWith(CrazyitProtocol.PRIVATE_ROUND) && line.endsWith(CrazyitProtocol.PRIVATE_ROUND)) {
                    //得到真實的訊息
                    String userAndMsg = getRealMsg(line);
                    //以SPLIT_SIGN分割字串,前半是私聊使用者,後半是聊天資訊
                    String user = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[0];
                    String msg = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[1];
                    //獲取私聊使用者對應的輸出流,並行送私聊資訊
                    Server.clients.map.get(user).println(Server.clients.getKeyByValue(ps) + "悄悄對你說:" + msg);
                }
                //公聊要向每一個Socket傳送
                else {
                    //得到真實訊息
                    String msg = getRealMsg(line);
                    //遍歷clients中的每個輸出流
                    for (PrintStream clientPs : Server.clients.valueSet()) {
                        clientPs.println(Server.clients.getKeyByValue(ps) + "說:" + msg);
                    }
                }
            }
        }
        //捕獲到異常後,表明Socket對應的使用者端已經出現了問題
        //所以程式將其對應的輸出流從Map中刪除
        catch (IOException e) {
            Server.clients.removeByValue(ps);
            System.out.println(Server.clients.map.size());
            //關閉網路,IO資源
            try {
                if (br != null) {
                    br.close();
                }
                if (ps != null) {
                    ps.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    //將讀到的內容去掉前後協定字元,恢復成真實資料
    private String getRealMsg(String line) {
        return line.substring(CrazyitProtocol.PROTOCOL_LEN,line.length()-CrazyitProtocol.PROTOCOL_LEN);
    }
}

Client類

package Test;

import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
    private static final int SERVER_PORT=30000;
    private Socket socket;
    private PrintStream ps;
    private BufferedReader brServer;
    private BufferedReader keyIn;
    public void init(){
        try
        {
            //初始化代表鍵盤的輸入流
            keyIn=new BufferedReader(new InputStreamReader(System.in));
            //連線到伺服器端
            socket=new Socket("127.0.0.1",SERVER_PORT);
            //獲取該Socket對應的輸入流和輸出流
            ps=new PrintStream(socket.getOutputStream());
            brServer=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String tip=" ";
            //採用不斷迴圈地彈出對話方塊要求輸入使用者名稱
            while(true){
                String userName= JOptionPane.showInputDialog(tip+"輸入使用者名稱");
                //使用者輸入的使用者名稱前後增加協定字串後傳送
                ps.println(CrazyitProtocol.USER_ROUND+userName+CrazyitProtocol.USER_ROUND);
                //讀取伺服器端的響應
                String result=brServer.readLine();
                //如果使用者名稱重複,則開始下一次迴圈
                if (result.equals(CrazyitProtocol.NAME_REP)){
                    tip="使用者名稱重複,請重試";
                    continue;
                }
                //伺服器端登入成功
                if (result.equals(CrazyitProtocol.LOGIN_SUCCESS)){
                    break;
                }
            }
        }
        //捕獲到異常,關閉網路資源,並退出該程式
        catch (UnknownHostException ex){
            System.out.println("找不到遠端伺服器,請確定伺服器已經啟動");
            closeRs();
            System.exit(1);
        }
        catch(IOException ex){
            System.out.println("網路異常,請重新登入");
            closeRs();
            System.exit(1);
        }
        //以該Socket對應的輸入流啟動ClientThread執行緒
        new ClientThread(brServer).start();
    }
    //定義一個讀取鍵盤輸出,並以網路傳送的方法
    private void readAndSend(){
        try
        {
            //不斷讀取鍵盤輸入
            String line=null;
            while ((line=keyIn.readLine())!=null){
                //如果傳送的訊號中有冒號,並以//開頭,則認為想傳送私聊資訊
                if (line.indexOf(":")>0&&line.startsWith("//")){
                    line=line.substring(2);
                    ps.println(CrazyitProtocol.PRIVATE_ROUND+line.split(":")[0]+CrazyitProtocol.SPLIT_SIGN+line.split(":")[1]+CrazyitProtocol.PRIVATE_ROUND);
                }
                else{
                    ps.println(CrazyitProtocol.MSG_ROUND+line+CrazyitProtocol.MSG_ROUND);
                }
            }
        }
        catch (IOException ex){
            System.out.println("網路通訊異常!請重新登入");
            closeRs();
            System.exit(1);
        }
    }
    //關閉Socket,輸入流,輸出流的方法
    private void closeRs(){
        try
        {
            if (keyIn!=null){
                ps.close();
            }
            if (brServer!=null){
                ps.close();
            }
            if (ps!=null){
                ps.close();
            }
            if (socket!=null){
                keyIn.close();
            }
        }
        catch (IOException ex){
            ex.printStackTrace();
        }
    }

    public static void main(String[] args){
        Client client=new Client();
        client.init();
        client.readAndSend();
    }
}

ClientThread類

package Test;

import java.io.BufferedReader;
import java.io.IOException;

public class ClientThread extends Thread {
    //該使用者端執行緒負責處理輸入流
    BufferedReader br=null;
    //使用一個網路輸入流來建立使用者端執行緒
    public ClientThread(BufferedReader br){
        this.br=br;
    }
    public void run(){
        try
        {
            String line=null;
            //不斷地從輸入流中讀取資料,並將這些資料列印輸出
            while((line=br.readLine())!=null){
                System.out.println(line);
            }
        }
        catch (IOException ex){
            ex.printStackTrace();
        }
        finally {
            try {
                if (br!=null){
                    br.close();
                }
            }
            catch (IOException ex){
                ex.printStackTrace();
            }
        }
    }
}

CrazyitMap類

package Test;

import java.util.*;

public class CrazyitMap<k,v> {
    //建立一個執行緒安全的HashMap
    public Map<k,v> map= Collections.synchronizedMap(new HashMap<k,v>());
    //根據value來刪除指定項
    public synchronized void removeByValue(Object value){
        for (Object key:map.keySet()){
            if (map.get(key)==value){
                map.remove(key);
                break;
            }
        }
    }
    //獲取所有value組成的Set集合
    public synchronized Set<v> valueSet(){
        Set<v> result=new HashSet<v>();
        //將map中的所有value新增到result集合中
        map.forEach((key,value)->result.add(value));
        return result;
    }
    //根據value查詢key
    public synchronized k getKeyByValue(v value){
        //遍歷所有key組成的集合
        for (k key:map.keySet()){
            //如果指定key對應的value與被搜尋的value相同,則返回對應的key
            if(map.get(key)==value||map.get(key).equals(value)){
                return key;
            }
        }
        return null;
    }
    //實現put()方法,該方法不允許value重複
    public synchronized v put(k key,v value){
        //遍歷所有value組成的集合
        for (v val:valueSet()){
            //如果某個value與試圖放入集合的value相同
            //則丟擲一個RuntimeException異常
            if (val.equals(value)&&val.hashCode()==value.hashCode()){
                throw new RuntimeException("MyMap範例不允許有重複的value");
            }
        }
        return map.put(key,value);
    }
}

CrazyitProtocol類

package Test;

public interface CrazyitProtocol {
    //定義協定字串的長度
    int PROTOCOL_LEN=2;
    //下面是一些協定字串,伺服器端和使用者端交換的資訊都應該在前後新增這種特殊字串
    String MSG_ROUND="ηθ";
    String USER_ROUND="∏∑";
    String LOGIN_SUCCESS="1";
    String NAME_REP="-1";
    String PRIVATE_ROUND="★【";
    String SPLIT_SIGN="卐";
}

執行結果:

開啟伺服器並且執行三個使用者端使用者名稱分別是xuwei,jiji和yaou

首先傳送一句公開資訊:
xuwei傳送了一句話

jiji收到:

yaou收到:

再傳送一句私聊資訊給jiji
xuwei傳送了一句悄悄話:

jiji收到:

yaou沒有收到:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com