1. 程式人生 > >Java通過Socket來實現簡單多人聊天室

Java通過Socket來實現簡單多人聊天室

Socket可以實現網路上兩個程式通過雙向通道進行資料的交換,此外它是Java中網路TCP/IP協議的封裝,例如可以進行網路通訊等等,下面我們就來簡單寫一下多人聊天室。

首先來分析一下要實現的流程

  • 首先建立一個伺服器端,構建ServerSocket並繫結埠
  • 建立socket客戶端,連線到指定ip以及其埠
  • 然後使用accept阻塞接收socket發出的連線請求
  • 獲取連線後的socket客戶端的輸入流和輸出流
  • 根據輸入流和輸出流進行兩者資料的通訊

值得一提是:該Socket是同步阻塞的,因此在socket客戶端需要進行建立一個執行緒,來分別進行向伺服器輸出,和接收伺服器傳輸的資料。要解決同步阻塞這個問題可以去了解JAVA NIO。

Socket客戶端程式碼如下:

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

public class  Client{

    public static void main(String[] args) throws IOException {
        //建立連線指定Ip和埠的socket
        Socket socket = new Socket("127.0.0.1",5200);
        //獲取系統標準輸入流
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //建立一個執行緒用於讀取伺服器的資訊
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true){
                        System.out.println(in.readLine());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        //寫資訊給客戶端
        String  line = reader.readLine();
        while (!"end".equalsIgnoreCase(line)){
            //將從鍵盤獲取的資訊給到伺服器
            out.println(line);
            out.flush();
            //顯示輸入的資訊
            line = reader.readLine();
        }
        out.close();
        in.close();
        socket.close();

    }
}

由於要接收多個客戶端的請求,因此服務端需要多個執行緒進行分別來接收客戶端的請求。

Socket服務端程式碼如下:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Vector;

public class Servers {
    //將接收到的socket變成一個集合
  protected static   List<Socket> sockets = new Vector<>();
  
  public static void main(String[] args) throws IOException {
        //建立服務端
        ServerSocket server = new ServerSocket(5200);
        boolean flag = true;
        //接受客戶端請求
        while (flag){
            try {
             //阻塞等待客戶端的連線
            Socket accept = server.accept();
            synchronized (sockets){
                sockets.add(accept);
            }
            //多個伺服器執行緒進行對客戶端的響應
            Thread thread = new Thread(new ServerThead(accept));
            thread.start();
            //捕獲異常。
            }catch (Exception e){
                flag = false;
                e.printStackTrace();
            }
        }
        //關閉伺服器
        server.close();
    }

}

Server執行緒程式碼如下:

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

/**
 * 伺服器執行緒,主要來處理多個客戶端的請求
 */
public class ServerThead extends Servers implements Runnable{

    Socket socket;
    String socketName;

    public ServerThead(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //設定該客戶端的端點地址
            socketName = socket.getRemoteSocketAddress().toString();
            System.out.println("[email protected]"+socketName+"已加入聊天");
            print("[email protected]"+socketName+"已加入聊天");
            boolean flag = true;
            while (flag)
            {
                //阻塞,等待該客戶端的輸出流
                String line = reader.readLine();
                //若客戶端退出,則退出連線。
                if (line == null){
                    flag = false;
                    continue;
                }
                String msg = "[email protected]"+socketName+":"+line;
                System.out.println(msg);
                //向線上客戶端輸出資訊
                print(msg);
            }

            closeConnect();
        } catch (IOException e) {
            try {
                closeConnect();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
    /**
     * 向所有線上客戶端socket轉發訊息
     * @param msg
     * @throws IOException
     */
    private void print(String msg) throws IOException {
        PrintWriter out = null;
        synchronized (sockets){
        for (Socket sc : sockets){
            out = new PrintWriter(sc.getOutputStream());
            out.println(msg);
            out.flush();
        }
    }
    }
    /**
     * 關閉該socket的連線
     * @throws IOException
     */
    public void closeConnect() throws IOException {
        System.out.println("[email protected]"+socketName+"已退出聊天");
        print("[email protected]"+socketName+"已退出聊天");
        //移除沒連線上的客戶端
        synchronized (sockets){
            sockets.remove(socket);
        }
        socket.close();
    }
}

由於要接收多個客戶端的資訊,並轉發到每一個已經連線上的客戶端,因此建立了一個Vector集合來儲存每一個客戶端Socket,由於是多個執行緒同時對這個Vector集合進行操作,因此加上synchronized關鍵字保證同步安全。

先執行伺服器端,然後在執行多個客戶端就可以進行多人聊天了。

下面是執行的結果。

客戶端2
客戶端1
客戶端3
服務端

大概就這樣子了,如果有什麼問題的,還請各位大佬多多指點一下,謝謝!