是不是找了很多類似的博文都不能實現各個博主展示的效果呢?
原因在這我不談,但是我保證,你看了這篇文章,只要你動手,那肯定有收穫,沒收穫,那就是我蠢了
一、截圖效果展示
有效果纔有動力,這裏我就先展示效果
服務端截圖:
客戶端截圖:
羣聊與私聊截圖:
二、動圖演示
這裏我直接開了三個客戶端,不會開的可以留言哈
客戶端要定住它,不然它老是被切換,不會定的也可留言呀!!!
三、原理分析:(很重要呀呀)
- 首先要知道客戶端與服務端的聯繫。
看我畫的圖:
說明分析:
- 流
1. 輸出流:OutputStream(發送數據)
2. 輸入流: InputStream(讀取數據)
3. 如果這個知識點你都不是很瞭解,那就去補補IO流知識哈
- 服務器端
- 小夥伴們,這裏你別把服務器端想的太過於複雜高大尚
- 這裏其實就是一個類,它主要是用來存儲數據、分發數據(我的理解)
- 然後就是一個無限循環,等待相應客戶端,就這麼簡單哈
- 多人聊天
- 多人聊天意味着服務端要響應多個客戶端,所以要在服務端開啓無限循環模式
- 因爲要多人聊天,所以就必須要加入多線程,不然就一直等待,直到某個客戶端退出
- 需要一個容器來管理客戶端,用它來進行各種操作(羣發消息、私聊,系統消息)
- 羣聊結構圖
分析說明:
- 服務端通過receive方法(自己封裝),獲取數據
- 然後遍歷容器,分發給除掉自身的socket,調用其send()方法(也需要自行封裝)
四、擼代碼(核心)
光說不練歪把子
代碼都有詳細的解釋,我不信你看不懂,爲了代碼的可維護性,和整潔性,我將其進行了封裝(其實就是各自寫個函數、或者寫成一個類而已,沒有什麼大不了的哈)
工具類:
package socket_study03;
import java.io.Closeable;
import java.io.IOException;
/**
* .工具類
* 用途:用於關閉各種流操作,封裝一些代碼
* @author 放牛娃學編程
*
*/
public class Utils {
//釋放資源(後邊帶...,它代表可變參數)
public static void release(Closeable...targets)
{
for(Closeable target: targets)
{
try
{
if(null != target)
{
target.close();
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
客戶端:
package socket_study03;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
/**
* 最終版:
* 1.用多線程實現客戶端
* 2.讀寫分開
* 3.封裝代碼
*
* 功能:羣聊、私聊
*
* 私聊格式說明:@xxx:msg
*
* @author 放牛娃學編程(公衆號)
*
*/
//發送(寫)線程類
class Send implements Runnable{
private BufferedReader console;
private DataOutputStream dos;
private Socket client;
private boolean flag;
private String name; //羣聊時的備註
//構造器(用於數據初始化)
public Send(Socket client, String name)
{
this.client = client;
flag = true;
this.name = name;
console = new BufferedReader(new InputStreamReader(System.in));
try {
dos = new DataOutputStream(client.getOutputStream());
//先將自己的備註發過去(服務端)
send(name);
} catch (IOException e) {
// TODO Auto-generated catch block
this.release();
}
}
//從控制檯獲取數據
public String getFromConsole()
{
String msg = "";
try {
msg = console.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
release();
}
return msg;
}
//發送消息
public void send(String msg)
{
if(!msg.equals(""))
{
try
{
dos.writeUTF(msg);
dos.flush();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
//釋放資源
public void release()
{
Utils.release(dos, client);
this.flag = false;
}
//線程體
@Override
public void run() {
// TODO Auto-generated method stub
while(flag)
{
//獲取控制檯輸入的消息
String msg = getFromConsole();
//發送消息
send(msg);
}
}
}
//接收(消息)線程類
class Receive implements Runnable{
private DataInputStream dis;
private boolean flag;
private Socket client;
//構造器,用於對數據的初始化
public Receive(Socket client)
{
this.client = client;
flag = true;
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
this.release();
}
}
//接收消息
public void receive()
{
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
// TODO Auto-generated catch block
release();
}
if(!msg.equals(""))
{
System.out.println(msg);
}
}
//釋放資源
//釋放資源
public void release()
{
Utils.release(dis, client);
this.flag = false;
}
//線程體
@Override
public void run() {
// TODO Auto-generated method stub
while(flag)
{
//接收消息
receive();
}
}
}
public class Client {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
System.out.println("-------這是客戶端-----------");
//獲取控制檯輸入流
BufferedReader br = new BufferedReader(new java.io.InputStreamReader(System.in));
System.out.println("進入羣聊前,請輸入你的備註");
String name = br.readLine();
//獲取socket管道
Socket client = new Socket(InetAddress.getLocalHost(), 9898);
//發送(寫)線程
new Thread(new Send(client, name)).start();
//接收(讀)線程
new Thread(new Receive(client)).start();
}
}
服務端:
限於篇幅這裏我只給出主線程的代碼(需要的自行提取)
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
System.out.println("-----這是服務端----------");
//獲取ServerSocket管道
ServerSocket sSocket = new ServerSocket(9898);
while(true)
{
//獲取客戶端的socket
Socket client = sSocket.accept();
System.out.println("一個客戶端連接成功");
Channel channel = new Channel(client);
//將每個客戶端添加到容器中,進行統一管理
all.add(channel);
//啓動線程
new Thread(channel).start();
}
}
五、 各種bug吐槽方案
- 直接複製這裏的代碼在eclipse中運行是報錯的? 因爲服務端的代碼不全呀!!
- 如何獲取服務端代碼或者是整個項目? 關注公衆號後回覆:羣聊項目。或者加我微信我親自發你也行。
- 只需要服務端代碼,如何獲取? 點這就有了:Java聊天室----多線程實現羣聊、私聊、系統消息 (服務端完整代碼奉上)
- 小夥伴們,這個聊天項目測試通過,能夠完成既定目標(羣聊、私聊、系統回覆)
如果遇到問題,沒關係,多去折騰就完事了,也歡迎小夥伴們留言交流學習。
六、分享交流
最後有興趣一起交流的,可以關注我的公衆號:這裏你能夠學到很實用的技巧,不是常用的我不說,公衆號回覆提取碼即可獲取以下學習資料啦啦啦啦,喜歡就拿去吧!!
(鏈接時常會失效,若出現此類情況,可以加我微信:17722328325(加時請備註:學習資料))
-
Java web從入門到精通電子書
-
Python機器學習電子書
-
Python400集(北京尚學堂)
-
JavaScript項目案例、經典面試題
-
Java300集(入門、精通)
-
Java後端培訓機構錄集(同事培訓內部提供)
-
IO流文檔
-
JavaEE面試題及其參考答案文檔
-
JavaSE面試題及其參考答案文檔
-
java多線程技術文檔
-
java網絡編程文檔
7~11都是之前同學花錢買來備戰找實習的,需要的我也分享出去了。