Java基于Socket實現(xiàn)多人聊天室_第1頁
Java基于Socket實現(xiàn)多人聊天室_第2頁
Java基于Socket實現(xiàn)多人聊天室_第3頁
Java基于Socket實現(xiàn)多人聊天室_第4頁
Java基于Socket實現(xiàn)多人聊天室_第5頁
已閱讀5頁,還剩15頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論