<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文範例為大家分享了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(); } } } } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45