版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
第Java基于Socket實現(xiàn)多人聊天室本文實例為大家分享了Java基于Socket實現(xiàn)簡易版多人聊天室的具體代碼,供大家參考,具體內(nèi)容如下
一、聊天室需求
1、一個服務(wù)端,多個客戶端;
2、實現(xiàn)客戶端和服務(wù)端的交互;
3、客戶端發(fā)送信息,服務(wù)端收到信息,再轉(zhuǎn)發(fā)給其他客戶端;
4、上下線時顯示哪個客戶端上下線并且顯示在線客戶端數(shù)量;
二、代碼分析
1.建立連接
客戶端類,創(chuàng)建發(fā)送端Socket對象,用自己的IP地址和端口號,與服務(wù)端建立連接。
classClient:
//用于與服務(wù)端通信的Socket
privateSocketsocket;
publicClient()throwsException{
/*
*初始化Socket的同時要制定服務(wù)端的IP地址和端口號
*ip地址用于我們在網(wǎng)絡(luò)上找到服務(wù)端的所在計算在
*端口號用于找到服務(wù)器上的服務(wù)端應(yīng)用程序。
*
*實例化Socket的過程就是連接服務(wù)端的過程若
*服務(wù)端無響應(yīng),這里得構(gòu)造方法得拋出異常。
*
*/
try{
System.out.println("正在連接服務(wù)器......");
//localhost
socket=newSocket("LAPTOP-TCK59O6Q",8888);
System.out.println("與服務(wù)端連接完畢");
}catch(Exceptione){
System.out.println("初始化失敗");
throwe;
}
}
服務(wù)端類,使用構(gòu)造方法初始化服務(wù)端,創(chuàng)建接收端的Socket對象
classServer:
privateServerSocketserver;
//構(gòu)造方法初始化服務(wù)端
publicServer()throwsIOException{
//實例化serverSocket的同時,指定服務(wù)端的端口號;
try{
server=newServerSocket(8888);
allOut=newArrayListPrintWriter
}catch(Exceptione){
System.out.println("服務(wù)端初始化失敗");
throwe;
}
}
2.客戶端發(fā)送信息
在客戶端的類中寫一個start()方法,start()是客戶端發(fā)送信息給服務(wù)端的方法
獲取輸出流對象,把鍵盤錄入的信息發(fā)送到服務(wù)端。
classClient:
publicvoidstart()throwsException{
/*
*客戶端開始工作的方法
*/
try{
//啟動用于讀取服務(wù)端發(fā)送消息的線程
ServerHandlerhandler=newServerHandler();
//ServerHandler是自己寫的類,實現(xiàn)Runnable接口,有多線程功能
Threadt=newThread(handler);
t.start();
//將數(shù)據(jù)發(fā)送到服務(wù)端
OutputStreamout=socket.getOutputStream();//獲取輸出流對象
OutputStreamWriterosw=newOutputStreamWriter(out,"utf-8");//轉(zhuǎn)化成utf-8格式
PrintWriterpw=newPrintWriter(osw,true);
Scannerscan=newScanner(System.in);
while(true){
Stringmessage=scan.nextLine();//得到鍵盤錄入的信息
pw.println(message);//把信息輸出到服務(wù)端
}
}catch(Exceptione){
System.out.println("客戶端運行失敗");
throwe;
}
}
服務(wù)端工作的start()方法,accept()方法與客戶端連接上
classServer:
//服務(wù)端工作的方法
publicvoidstart()throwsIOException{
/*
*ServerSocket提供了一個accept的方法,該方法是一個阻塞方法,
*用于監(jiān)聽其打開的8888端口;當(dāng)一個客戶端通過該端口與服務(wù)端連接時,
*accept方法就會解除阻塞,然后創(chuàng)建一個socket實例并返回,
*socket的作用就是與剛剛連接上的客戶端進行通信。
*/
while(true){
System.out.println("等待客戶端連接...");
Socketsocket=server.accept();
System.out.println("一個客戶端連接了!");
//啟動一個線程來處理客戶端的交互工作
ClientHandlerhander=newClientHandler(socket);
Threadt=newThread(hander);
t.start();
}
}
3.開啟多線程、服務(wù)端接收讀取信息并廣播
因為服務(wù)端與多個客戶端相連,所以要用多線程,即一個客戶端用一條線程。
在服務(wù)端類中創(chuàng)建一個內(nèi)部類ClientHandler實現(xiàn)Runnable接口并重寫run()方法創(chuàng)建線程
屬性有客戶端的Socket對象
有參構(gòu)造方法中通過客戶端的Socket獲取到其地址host,并且把地址打印出來
這樣在main()方法中,實例化服務(wù)端類的對象之后,start方法開啟服務(wù)端,當(dāng)有客戶端連接上時,就能輸出這個客戶端的ip地址。
ClientHandler類要重寫run()方法,使用輸入流InputStream讀取客戶端發(fā)來的信息,再使用輸出流OutputStream給所有客戶端廣播收到的信息、用戶上下線和在線人數(shù)
classServer:
/**
*ClientHandler
*該線程類是與指定的客戶端進行交互工作;
*@authorzxm
classClientHandlerimplementsRunnable{
//當(dāng)前線程客戶端的Socket
privateSocketsocket;
//該客戶端的地址
privateStringhost;
publicClientHandler(Socketsocket){
this.socket=socket;
/*
*通過socket獲取遠(yuǎn)程計算機地址
*對于服務(wù)端而言,遠(yuǎn)程計算機就是客戶端
*/
InetAddressaddress=socket.getInetAddress();
//獲取ip地址
host=address.getHostAddress();
System.out.println("host"+host);
}
@Override
publicvoidrun(){
PrintWriterpw=null;
try{
//廣播給所有客戶端,當(dāng)前用戶上線了
sendMessage("["+host+"]上線了");
OutputStreamout=socket.getOutputStream();
OutputStreamWriterosw=newOutputStreamWriter(out,"UTF-8");
pw=newPrintWriter(osw,true);
//將該客戶的輸出流存入共享集合,以便消息可以廣播給該客戶端
addOut(pw);
//廣播當(dāng)前在線人數(shù)
sendMessage("當(dāng)前在線人數(shù)["+allOut.size()+"]");
//處理來自客戶端的數(shù)據(jù)
InputStreamin=socket.getInputStream();
InputStreamReaderisr=newInputStreamReader(in,"utf-8");
BufferedReaderbr=newBufferedReader(isr);
/*
*服務(wù)端讀取客戶端發(fā)送過來的每一句字符時
*由于客戶端所在的操作系統(tǒng)不同,這里客戶端斷開時結(jié)果也不同
*windows客戶端開始br.readLine拋出異常
*Linux客戶端斷開是返回null
*
*/
Stringmessage=null;
while((message=br.readLine())!=null){
sendMessage(host+"說:"+message);
}
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}finally{
//將該客戶端的輸出流從共享集合中刪除
removeOut(pw);
//廣播給所有客戶端,當(dāng)前用戶下線
sendMessage("["+host+"]下線了");
//廣播當(dāng)前在線人數(shù)
sendMessage("當(dāng)前在線人數(shù)["+allOut.size()+"]");
try{
socket.close();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
}
因此服務(wù)端類中要寫一個sendMessage()方法,為了在接收到一個客戶端的信息后,把這個信息轉(zhuǎn)發(fā)給所有客戶端,這就是廣播的效果。
寫一個集合allOut,用來存放所有客戶端的輸出流,客戶端的數(shù)量就是集合里元素的個數(shù)
再寫兩個方法,一個是addOut()方法把上線客戶端的輸出流放進集合(在run方法中使用addOut(),獲取到啟用新線程的客戶端的輸出流,把輸出流加到集合中),
另一個是removeOut()方法拿出集合(同理run()方法中使用,把socket關(guān)閉的客戶端的輸出流移除集合)。
所以sendMessage()方法的參數(shù)就是某個客戶端發(fā)的字符串信息message,遍歷allOut集合,把message在每個輸出流中打印,用PrintWrite類中的print方法。
當(dāng)客戶端連接服務(wù)端時,sendMessage()方法打印這個服務(wù)端的地址加上上線了,同理客戶端關(guān)閉socket的時候打印下線了,
同時上下線后再打印allOut集合的大小,也就是當(dāng)前連接服務(wù)端的客戶端數(shù)量,就是在線人數(shù)。
classServer:
//存放所有客戶端的輸出流的集合,用于廣播
privateListPrintWriterallOut;
//將給定的輸出流放入共享集合
privatesynchronizedvoidaddOut(PrintWriterout){
allOut.add(out);
//將給定的輸出流移除共享集合
privatesynchronizedvoidremoveOut(PrintWriterout){
allOut.remove(out);
//將給定的消息發(fā)給多個客戶端
privatesynchronizedvoidsendMessage(Stringmessage){
for(PrintWriterout:allOut){
out.println(message);
}
}
4.客戶端讀取信息
這個時候所有的客戶端都收到了某個客戶發(fā)的消息,但是還沒讀,所以客戶端類中要加輸入流才能讀取,
創(chuàng)建ServerHandler類實現(xiàn)Runnable接口,輸入流讀取并輸出。
classClient:
classServerHandlerimplementsRunnable{
/**
*該線程用于讀取服務(wù)端發(fā)送過來的消息,并輸出到
*客戶端的控制臺上
*@authorzxm
*
*/
@Override
publicvoidrun(){
try{
InputStreamin=socket.getInputStream();//輸入流
InputStreamReaderisr=newInputStreamReader(in,"UTF-8");//以utf-8讀
BufferedReaderbr=newBufferedReader(isr);
Stringmessage=null;
while((message=br.readLine())!=null){
System.out.println(message);
}
}catch(Exceptione){
e.printStackTrace();
}
}
}
三、完整代碼
1.客戶端
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.InputStreamReader;
importjava.io.OutputStream;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.io.UnsupportedEncodingException;
import.Socket;
import.UnknownHostException;
importjava.util.Scanner;
*聊天室服務(wù)端
*@authorzxm
publicclassClient{
//用于與服務(wù)端通信的Socket
privateSocketsocket;
publicClient()throwsException{
/*
*初始化Socket的同時要制定服務(wù)端的IP地址和端口號
*ip地址用于我們在網(wǎng)絡(luò)上找到服務(wù)端的所在計算在
*端口號用于找到服務(wù)器上的服務(wù)端應(yīng)用程序。
*
**實例化Socket的過程就是連接服務(wù)端的過程若
*服務(wù)端無響應(yīng),這里得構(gòu)造方法得拋出異常。
*
*/
try{
System.out.println("正在連接服務(wù)器......");
//localhost
socket=newSocket("LAPTOP-TCK59O6Q",8888);
System.out.println("與服務(wù)端連接完畢");
}catch(Exceptione){
System.out.println("初始化失敗");
throwe;
}
}
publicvoidstart()throwsException{
/*
*客戶端開始工作的方法
*/
try{
//啟動用于讀取服務(wù)端發(fā)送消息的線程
ServerHandlerhandler=newServerHandler();
//ServerHandler是自己寫的類,實現(xiàn)Runnable接口,有多線程功能
Threadt=newThread(handler);
t.start();
//將數(shù)據(jù)發(fā)送到服務(wù)端
OutputStreamout=socket.getOutputStream();//獲取輸出流對象
OutputStreamWriterosw=newOutputStreamWriter(out,"utf-8");//轉(zhuǎn)化成utf-8格式
PrintWriterpw=newPrintWriter(osw,true);
Scannerscan=newScanner(System.in);
while(true){
Stringmessage=scan.nextLine();//得到鍵盤錄入的信息
pw.println(message);//把信息輸出到服務(wù)端
}
}catch(Exceptione){
System.out.println("客戶端運行失敗");
throwe;
}
}
publicstaticvoidmain(String[]args)throwsException{
try{
Clientclient=newClient();
client.start();
}catch(Exceptione){
System.out.println("客戶端運行失敗");
e.printStackTrace();
}
}
classServerHandlerimplementsRunnable{
/**
*該線程用于讀取服務(wù)端發(fā)送過來的消息,并輸出到
*客戶端的控制臺上
*@authorzxm
*
*/
@Override
publicvoidrun(){
try{
InputStreamin=socket.getInputStream();//輸入流
InputStreamReaderisr=newInputStreamReader(in,"UTF-8");//以utf-8讀
BufferedReaderbr=newBufferedReader(isr);
Stringmessage=null;
while((message=br.readLine())!=null){
System.out.println(message);
}
}catch(Exceptione){
e.printStackTrace();
}
}
}
}
2.服務(wù)端
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.InputStreamReader;
importjava.io.OutputStream;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.io.UnsupportedEncodingException;
import.InetAddress;
import.ServerSocket;
import.Socket;
importjava.util.ArrayList;
importjava.util.List;
*聊天室服務(wù)端
*@authorzxm
publicclassServer{
/*
*運行在服務(wù)端的socket
*該類的作用是:
*
1.申請服務(wù)端口,客戶端就是通過它申請的服務(wù)端口連接上服務(wù)端應(yīng)用的。
*
2.監(jiān)聽申請的服務(wù)端口感知客戶端的連接,并創(chuàng)建一個socket與該客戶通信。
*/
privateServerSocketserver;
//存放所有客戶端的輸出流的集合,用于廣播
privateListPrintWriterallOut;
//將給定的輸出流放入共享集合
privatesynchronizedvoidaddOut(PrintWriterout){
allOut.add(out);
}
//將給定的輸出流移除共享集合
privatesynchronizedvoidremoveOut(PrintWriterout){
allOut.remove(out);
}
//將給定的消息發(fā)給多個客戶端
privatesynchronizedvoidsendMessage(Stringmessage){
for(PrintWriterout:allOut){
out.println(message);
}
}
//構(gòu)造方法初始化服務(wù)端
publicServer()throwsIOException{
//實例化serverSocket的同時,指定服務(wù)端的端口號;
try{
server=newServerSocket(8888);
allOut=newArrayListPrintWriter
}catch(Exceptione){
System.out.println("服務(wù)端初始化失敗");
throwe;
}
}
//服務(wù)端工作的方法
publicvoidstart()throwsIOException{
/*
*ServerSocket提供了一個accept的方法,該方法是一個阻塞方法,
*用于監(jiān)聽其打開的8888端口;當(dāng)一個客戶端通過該端口與服務(wù)端連接時,
*accept方法就會解除阻塞,然后創(chuàng)建一個socket實例并返回,
*socket的作用就是與剛剛連接上的客戶端進行通信。
*/
while(true){
System.out.println("等待客戶端連接...");
Socketsocket=server.accept();
System.out.println("一個客戶端連接了!");
//啟動一個線程來處理客戶端的交互工作
ClientHandlerhander=newClientHandler(socket);
Threadt=newThread(hander);
t.start();
}
}
publicstaticvoidmain(String[]args)throwsException{
Serverserver=newServer();
server.start();
}
/**
*該線程類是與指定的客戶端進行交互工作;
*@authorzxm
*
*/
classClientHandlerimplementsRunnable{
//當(dāng)前線程客戶端的Socket
privateSocketsocket;
//該客戶端的地址
privateStringhost;
publicClientHandler(Socketsocket){
this.socket=socket;
/*
*通過socket獲取遠(yuǎn)程計算機地址
*對于服務(wù)端而言,遠(yuǎn)程計算機就是客戶端
*/
InetAddressaddress=socket.getInetAddress();
//獲取ip地址
host=address.getHostAddress();
System.out.println("host"+host);
}
@Override
publicvoidrun(){
PrintWriterpw=null;
try{
//廣播給所有客戶端,當(dāng)前用戶上線了
sendMessage("["+host+"]上線了");
OutputStreamout=socket.getOutputStream();
OutputStreamWriterosw=newOutputStreamWriter(out,"UTF-8");
pw=newPrintWriter(osw,true);
//將該客戶的輸出流存入共享集合,以便消息可以廣播給該客戶端
addOut(pw);
//廣播當(dāng)前在線人數(shù)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 外墻防水施工技術(shù)方案
- 水電設(shè)備運行監(jiān)測方案
- 竣工驗收多方聯(lián)動協(xié)調(diào)方案
- 開挖過程中土體變形監(jiān)測方案
- 養(yǎng)老護理員職業(yè)素養(yǎng):服裝篇
- ANCA相關(guān)性腎炎的護理研究進展
- 隧道周邊環(huán)境保護技術(shù)方案
- 2026年及未來5年市場數(shù)據(jù)中國美術(shù)用品行業(yè)發(fā)展前景預(yù)測及投資戰(zhàn)略咨詢報告
- 儲備糧倉庫文化建設(shè)與推廣方案
- 2026年及未來5年市場數(shù)據(jù)中國分割羊肉行業(yè)發(fā)展監(jiān)測及投資戰(zhàn)略數(shù)據(jù)分析研究報告
- 2025-2026學(xué)年北京市西城區(qū)高三(上期)期末考試地理試卷(含答案詳解)
- 贛州市章貢區(qū)2026年社區(qū)工作者(專職網(wǎng)格員)招聘【102人】考試參考題庫及答案解析
- 江蘇高職單招培訓(xùn)課件
- 2026年山東理工職業(yè)學(xué)院單招綜合素質(zhì)考試參考題庫帶答案解析
- 2026年及未來5年市場數(shù)據(jù)中國氟樹脂行業(yè)發(fā)展?jié)摿Ψ治黾巴顿Y方向研究報告
- DB1331∕T 109-2025 雄安新區(qū)建設(shè)工程抗震設(shè)防標(biāo)準(zhǔn)
- DB37∕T 1317-2025 超細(xì)干粉滅火系統(tǒng)技術(shù)規(guī)范
- Scratch講座課件教學(xué)課件
- 《低碳醫(yī)院評價指南》(T-SHWSHQ 14-2025)
- 2025至2030中國砷化鎵太陽能電池外延片行業(yè)市場深度研究與戰(zhàn)略咨詢分析報告
- 質(zhì)量環(huán)境及職業(yè)健康安全三體系風(fēng)險和機遇識別評價分析及控制措施表(包含氣候變化)
評論
0/150
提交評論