Java通過Socket來實現簡單多人聊天室
阿新 • • 發佈:2018-12-13
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關鍵字保證同步安全。
先執行伺服器端,然後在執行多個客戶端就可以進行多人聊天了。
下面是執行的結果。
大概就這樣子了,如果有什麼問題的,還請各位大佬多多指點一下,謝謝!