您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Java基于Socket如何實現多人聊天室”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Java基于Socket如何實現多人聊天室”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
1、一個服務端,多個客戶端;
2、實現客戶端和服務端的交互;
3、客戶端發送信息,服務端收到信息,再轉發給其他客戶端;
4、上下線時顯示哪個客戶端上下線并且顯示在線客戶端數量;
1. 建立連接
客戶端類,創建發送端Socket對象,用自己的IP地址和端口號,與服務端建立連接。
class Client:
//用于與服務端通信的Socket private Socket socket; public Client() throws Exception { /* * 初始化Socket的同時要制定服務端的IP地址和端口號 * ip地址用于我們在網絡上找到服務端的所在計算在 * 端口號用于找到服務器上的服務端應用程序。 * * 實例化Socket的過程就是連接服務端的過程若 * 服務端無響應,這里得構造方法得拋出異常。 * */ try { System.out.println("正在連接服務器......"); //localhost 127.0.0.1 socket = new Socket("LAPTOP-TCK59O6Q",8888); System.out.println("與服務端連接完畢"); } catch (Exception e) { System.out.println("初始化失敗"); throw e; } }
服務端類,使用構造方法初始化服務端,創建接收端的Socket對象
class Server:
private ServerSocket server; //構造方法初始化服務端 public Server() throws IOException { //實例化serverSocket的同時,指定服務端的端口號; try { server = new ServerSocket(8888); allOut = new ArrayList<PrintWriter>(); } catch (Exception e) { System.out.println("服務端初始化失敗"); throw e; } }
2. 客戶端發送信息
在客戶端的類中寫一個start()方法,start()是客戶端發送信息給服務端的方法
獲取輸出流對象,把鍵盤錄入的信息發送到服務端。
class Client:
public void start() throws Exception { /* * 客戶端開始工作的方法 */ try { //啟動用于讀取服務端發送消息的線程 ServerHandler handler = new ServerHandler(); //ServerHandler是自己寫的類,實現Runnable接口,有多線程功能 Thread t = new Thread(handler); t.start(); //將數據發送到服務端 OutputStream out = socket.getOutputStream();//獲取輸出流對象 OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");//轉化成utf-8格式 PrintWriter pw = new PrintWriter(osw,true); Scanner scan = new Scanner(System.in); while(true) { String message = scan.nextLine();//得到鍵盤錄入的信息 pw.println(message);//把信息輸出到服務端 } } catch (Exception e) { System.out.println("客戶端運行失敗"); throw e; } }
服務端工作的start()方法,accept()方法與客戶端連接上
class Server:
//服務端工作的方法 public void start() throws IOException { /* * ServerSocket提供了一個accept的方法,該方法是一個阻塞方法, * 用于監聽其打開的8888端口;當一個客戶端通過該端口與服務端連接時, * accept方法就會解除阻塞,然后創建一個socket實例并返回, * socket的作用就是與剛剛連接上的客戶端進行通信。 */ while(true) { System.out.println("等待客戶端連接..."); Socket socket = server.accept(); System.out.println("一個客戶端連接了!"); //啟動一個線程來處理客戶端的交互工作 ClientHandler hander = new ClientHandler(socket); Thread t = new Thread(hander); t.start(); } }
3. 開啟多線程、服務端接收讀取信息并廣播
因為服務端與多個客戶端相連,所以要用多線程,即一個客戶端用一條線程。
在服務端類中創建一個內部類ClientHandler實現Runnable接口并重寫run()方法創建線程
屬性有客戶端的Socket對象
有參構造方法中通過客戶端的Socket獲取到其地址host,并且把地址打印出來
這樣在main()方法中,實例化服務端類的對象之后,start方法開啟服務端,當有客戶端連接上時,就能輸出這個客戶端的ip地址。
ClientHandler類要重寫run()方法,使用輸入流InputStream讀取客戶端發來的信息,再使用輸出流OutputStream給所有客戶端廣播收到的信息、用戶上下線和在線人數
class Server:
/** * ClientHandler * 該線程類是與指定的客戶端進行交互工作; * @author zxm * */ class ClientHandler implements Runnable{ //當前線程客戶端的Socket private Socket socket; //該客戶端的地址 private String host; public ClientHandler(Socket socket) { this.socket=socket; /* * 通過socket獲取遠程計算機地址 * 對于服務端而言,遠程計算機就是客戶端 */ InetAddress address = socket.getInetAddress(); //獲取ip地址 host = address.getHostAddress(); System.out.println("host"+host); } @Override public void run() { PrintWriter pw = null; try { //廣播給所有客戶端,當前用戶上線了 sendMessage("["+host+"]上線了"); OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8"); pw = new PrintWriter(osw,true); //將該客戶的輸出流存入共享集合,以便消息可以廣播給該客戶端 addOut(pw); //廣播當前在線人數 sendMessage("當前在線人數["+allOut.size()+"]"); //處理來自客戶端的數據 InputStream in = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(in,"utf-8"); BufferedReader br = new BufferedReader(isr); /* * 服務端讀取客戶端發送過來的每一句字符時 * 由于客戶端所在的操作系統不同,這里客戶端斷開時結果也不同 * windows客戶端開始br.readLine拋出異常 * Linux客戶端斷開是返回null * */ String message = null; while((message = br.readLine())!=null) { sendMessage(host+"說:"+message); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //將該客戶端的輸出流從共享集合中刪除 removeOut(pw); //廣播給所有客戶端,當前用戶下線 sendMessage("["+host+"]下線了"); //廣播當前在線人數 sendMessage("當前在線人數["+allOut.size()+"]"); try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
因此服務端類中要寫一個sendMessage()方法,為了在接收到一個客戶端的信息后,把這個信息轉發給所有客戶端,這就是廣播的效果。
寫一個集合allOut,用來存放所有客戶端的輸出流,客戶端的數量就是集合里元素的個數
再寫兩個方法,一個是addOut()方法把上線客戶端的輸出流放進集合(在run方法中使用addOut(),獲取到啟用新線程的客戶端的輸出流,把輸出流加到集合中),
另一個是removeOut()方法拿出集合(同理run()方法中使用,把socket關閉的客戶端的輸出流移除集合)。
所以sendMessage()方法的參數就是某個客戶端發的字符串信息message,遍歷allOut集合,把message在每個輸出流中打印,用PrintWrite類中的print方法。
當客戶端連接服務端時,sendMessage()方法打印這個服務端的地址加上上線了,同理客戶端關閉socket的時候打印下線了,
同時上下線后再打印allOut集合的大小,也就是當前連接服務端的客戶端數量,就是在線人數。
class Server:
//存放所有客戶端的輸出流的集合,用于廣播 private List<PrintWriter> allOut; //將給定的輸出流放入共享集合 private synchronized void addOut(PrintWriter out){ allOut.add(out); } //將給定的輸出流移除共享集合 private synchronized void removeOut(PrintWriter out){ allOut.remove(out); } //將給定的消息發給多個客戶端 private synchronized void sendMessage(String message) { for(PrintWriter out:allOut) { out.println(message); } }
4. 客戶端讀取信息
這個時候所有的客戶端都收到了某個客戶發的消息,但是還沒讀,所以客戶端類中要加輸入流才能讀取,
創建ServerHandler類實現Runnable接口,輸入流讀取并輸出。
class Client:
class ServerHandler implements Runnable{ /** * 該線程用于讀取服務端發送過來的消息,并輸出到 * 客戶端的控制臺上 * @author zxm * */ @Override public void run() { try { InputStream in = socket.getInputStream();//輸入流 InputStreamReader isr = new InputStreamReader(in,"UTF-8");//以utf-8讀 BufferedReader br = new BufferedReader(isr); String message = null; while((message=br.readLine())!=null) { System.out.println(message); } } catch (Exception e) { e.printStackTrace(); } } }
1. 客戶端
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner; /** * 聊天室服務端 * @author zxm */ public class Client { //用于與服務端通信的Socket private Socket socket; public Client() throws Exception { /* * 初始化Socket的同時要制定服務端的IP地址和端口號 * ip地址用于我們在網絡上找到服務端的所在計算在 * 端口號用于找到服務器上的服務端應用程序。 * * *實例化Socket的過程就是連接服務端的過程若 * 服務端無響應,這里得構造方法得拋出異常。 * */ try { System.out.println("正在連接服務器......"); //localhost 127.0.0.1 socket = new Socket("LAPTOP-TCK59O6Q",8888); System.out.println("與服務端連接完畢"); } catch (Exception e) { System.out.println("初始化失敗"); throw e; } } public void start() throws Exception { /* * 客戶端開始工作的方法 */ try { //啟動用于讀取服務端發送消息的線程 ServerHandler handler = new ServerHandler(); //ServerHandler是自己寫的類,實現Runnable接口,有多線程功能 Thread t = new Thread(handler); t.start(); //將數據發送到服務端 OutputStream out = socket.getOutputStream();//獲取輸出流對象 OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");//轉化成utf-8格式 PrintWriter pw = new PrintWriter(osw,true); Scanner scan = new Scanner(System.in); while(true) { String message = scan.nextLine();//得到鍵盤錄入的信息 pw.println(message);//把信息輸出到服務端 } } catch (Exception e) { System.out.println("客戶端運行失敗"); throw e; } } public static void main(String[] args) throws Exception { try { Client client = new Client(); client.start(); } catch (Exception e) { System.out.println("客戶端運行失敗"); e.printStackTrace(); } } class ServerHandler implements Runnable{ /** * 該線程用于讀取服務端發送過來的消息,并輸出到 * 客戶端的控制臺上 * @author zxm * */ @Override public void run() { try { InputStream in = socket.getInputStream();//輸入流 InputStreamReader isr = new InputStreamReader(in,"UTF-8");//以utf-8讀 BufferedReader br = new BufferedReader(isr); String message = null; while((message=br.readLine())!=null) { System.out.println(message); } } catch (Exception e) { e.printStackTrace(); } } } }
2. 服務端
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; /** * 聊天室服務端 * @author zxm */ public class Server { /* * 運行在服務端的socket * 該類的作用是: * 1.申請服務端口,客戶端就是通過它申請的服務端口連接上服務端應用的。 * 2.監聽申請的服務端口感知客戶端的連接,并創建一個socket與該客戶通信。 */ private ServerSocket server; //存放所有客戶端的輸出流的集合,用于廣播 private List<PrintWriter> allOut; //將給定的輸出流放入共享集合 private synchronized void addOut(PrintWriter out){ allOut.add(out); } //將給定的輸出流移除共享集合 private synchronized void removeOut(PrintWriter out){ allOut.remove(out); } //將給定的消息發給多個客戶端 private synchronized void sendMessage(String message) { for(PrintWriter out:allOut) { out.println(message); } } //構造方法初始化服務端 public Server() throws IOException { //實例化serverSocket的同時,指定服務端的端口號; try { server = new ServerSocket(8888); allOut = new ArrayList<PrintWriter>(); } catch (Exception e) { System.out.println("服務端初始化失敗"); throw e; } } //服務端工作的方法 public void start() throws IOException { /* * ServerSocket提供了一個accept的方法,該方法是一個阻塞方法, * 用于監聽其打開的8888端口;當一個客戶端通過該端口與服務端連接時, * accept方法就會解除阻塞,然后創建一個socket實例并返回, * socket的作用就是與剛剛連接上的客戶端進行通信。 */ while(true) { System.out.println("等待客戶端連接..."); Socket socket = server.accept(); System.out.println("一個客戶端連接了!"); //啟動一個線程來處理客戶端的交互工作 ClientHandler hander = new ClientHandler(socket); Thread t = new Thread(hander); t.start(); } } public static void main(String[] args) throws Exception { Server server = new Server(); server.start(); } /** * 該線程類是與指定的客戶端進行交互工作; * @author zxm * */ class ClientHandler implements Runnable{ //當前線程客戶端的Socket private Socket socket; //該客戶端的地址 private String host; public ClientHandler(Socket socket) { this.socket=socket; /* * 通過socket獲取遠程計算機地址 * 對于服務端而言,遠程計算機就是客戶端 */ InetAddress address = socket.getInetAddress(); //獲取ip地址 host = address.getHostAddress(); System.out.println("host"+host); } @Override public void run() { PrintWriter pw = null; try { //廣播給所有客戶端,當前用戶上線了 sendMessage("["+host+"]上線了"); OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8"); pw = new PrintWriter(osw,true); //將該客戶的輸出流存入共享集合,以便消息可以廣播給該客戶端 addOut(pw); //廣播當前在線人數 sendMessage("當前在線人數["+allOut.size()+"]"); //處理來自客戶端的數據 InputStream in = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(in,"utf-8"); BufferedReader br = new BufferedReader(isr); /* * 服務端讀取客戶端發送過來的每一句字符時 * 由于客戶端所在的操作系統不同,這里客戶端斷開時結果也不同 * windows客戶端開始br.readLine拋出異常 * Linux客戶端斷開是返回null * */ String message = null; while((message = br.readLine())!=null) { sendMessage(host+"說:"+message); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //將該客戶端的輸出流從共享集合中刪除 removeOut(pw); //廣播給所有客戶端,當前用戶下線 sendMessage("["+host+"]下線了"); //廣播當前在線人數 sendMessage("當前在線人數["+allOut.size()+"]"); try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
讀到這里,這篇“Java基于Socket如何實現多人聊天室”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。