App下載

基于Socket多人聊天室 Java代碼實(shí)現(xiàn)

猿友 2021-07-15 12:02:23 瀏覽數(shù) (2871)
反饋

本篇文章和大家一起分享Java基于Socket來實(shí)現(xiàn)多人聊天室的具體代碼,詳細(xì)內(nèi)容如下:

Socket可以實(shí)現(xiàn)網(wǎng)絡(luò)上兩個(gè)程序通過雙向通道進(jìn)行數(shù)據(jù)的交換,此外它是Java中網(wǎng)絡(luò)TCP/IP協(xié)議的封裝,例如可以進(jìn)行網(wǎng)絡(luò)通信等等,下面我們就來簡(jiǎn)單寫一下多人聊天室。

首先來分析一下要實(shí)現(xiàn)的流程

  • 首先建立一個(gè)服務(wù)器端,構(gòu)建ServerSocket并綁定端口
  • 創(chuàng)建socket客戶端,連接到指定ip以及其端口
  • 然后使用accept阻塞接收socket發(fā)出的連接請(qǐng)求
  • 獲取連接后的socket客戶端的輸入流和輸出流
  • 根據(jù)輸入流和輸出流進(jìn)行兩者數(shù)據(jù)的通信

值得一提是:該Socket是同步阻塞的,因此在socket客戶端需要進(jìn)行創(chuàng)建一個(gè)線程,來分別進(jìn)行向服務(wù)器輸出,和接收服務(wù)器傳輸?shù)臄?shù)據(jù)。要解決同步阻塞這個(gè)問題可以去了解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 {
 //創(chuàng)建連接指定Ip和端口的socket
 Socket socket = new Socket("127.0.0.1",5200);
 //獲取系統(tǒng)標(biāo)準(zhǔn)輸入流
 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 PrintWriter out = new PrintWriter(socket.getOutputStream());
 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 //創(chuàng)建一個(gè)線程用于讀取服務(wù)器的信息
 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)){
  //將從鍵盤獲取的信息給到服務(wù)器
  out.println(line);
  out.flush();
  //顯示輸入的信息
  line = reader.readLine();
 }
 out.close();
 in.close();
 socket.close();
 
 }
}

由于要接收多個(gè)客戶端的請(qǐng)求,因此服務(wù)端需要多個(gè)線程進(jìn)行分別來接收客戶端的請(qǐng)求。

Socket服務(wù)端代碼如下:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Vector;
 
public class Servers {
 //將接收到的socket變成一個(gè)集合
 protected static List<Socket> sockets = new Vector<>();
 
 public static void main(String[] args) throws IOException {
 //創(chuàng)建服務(wù)端
 ServerSocket server = new ServerSocket(5200);
 boolean flag = true;
 //接受客戶端請(qǐng)求
 while (flag){
  try {
  //阻塞等待客戶端的連接
  Socket accept = server.accept();
  synchronized (sockets){
  sockets.add(accept);
  }
  //多個(gè)服務(wù)器線程進(jìn)行對(duì)客戶端的響應(yīng)
  Thread thread = new Thread(new ServerThead(accept));
  thread.start();
  //捕獲異常。
  }catch (Exception e){
  flag = false;
  e.printStackTrace();
  }
 }
 //關(guān)閉服務(wù)器
 server.close();
 }
 
}

Server線程代碼如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
 
/**
 * 服務(wù)器線程,主要來處理多個(gè)客戶端的請(qǐng)求
 */
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()));
  //設(shè)置該客戶端的端點(diǎn)地址
  socketName = socket.getRemoteSocketAddress().toString();
  System.out.println("Client@"+socketName+"已加入聊天");
  print("Client@"+socketName+"已加入聊天");
  boolean flag = true;
  while (flag)
  {
  //阻塞,等待該客戶端的輸出流
  String line = reader.readLine();
  //若客戶端退出,則退出連接。
  if (line == null){
   flag = false;
   continue;
  }
  String msg = "Client@"+socketName+":"+line;
  System.out.println(msg);
  //向在線客戶端輸出信息
  print(msg);
  }
 
  closeConnect();
 } catch (IOException e) {
  try {
  closeConnect();
  } catch (IOException e1) {
  e1.printStackTrace();
  }
 }
 }
 /**
 * 向所有在線客戶端socket轉(zhuǎn)發(fā)消息
 * @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();
 }
 }
 }
 /**
 * 關(guān)閉該socket的連接
 * @throws IOException
 */
 public void closeConnect() throws IOException {
 System.out.println("Client@"+socketName+"已退出聊天");
 print("Client@"+socketName+"已退出聊天");
 //移除沒連接上的客戶端
 synchronized (sockets){
  sockets.remove(socket);
 }
 socket.close();
 }
}

由于要接收多個(gè)客戶端的信息,并轉(zhuǎn)發(fā)到每一個(gè)已經(jīng)連接上的客戶端,因此創(chuàng)建了一個(gè)Vector集合來保存每一個(gè)客戶端Socket,由于是多個(gè)線程同時(shí)對(duì)這個(gè)Vector集合進(jìn)行操作,因此加上synchronized關(guān)鍵字保證同步安全。

先運(yùn)行服務(wù)器端,然后在運(yùn)行多個(gè)客戶端就可以進(jìn)行多人聊天了。

下面是運(yùn)行的結(jié)果。

客戶端1

客戶端1

客戶端2

客戶端2

客戶端3

客戶端3

微信截圖_20210715115033

服務(wù)端

以上就是基于Socket多人聊天室,Java代碼實(shí)現(xiàn)的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。想要了解更多關(guān)于Java的內(nèi)容,請(qǐng)搜索W3Cschool以往的文章或者繼續(xù)瀏覽接下來的文章。


0 人點(diǎn)贊