【Java网络编程(四)】手写TCP聊天室——控制台版
生活随笔
收集整理的這篇文章主要介紹了
【Java网络编程(四)】手写TCP聊天室——控制台版
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
版本1:群聊功能
使用TCP
運行方式:先開服務端,再開任意個數客戶端。在控制臺可以實現群聊功能。
效果
代碼
服務端
客戶端
package cn.hanquan.groupchat;import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner;/** 在線聊天室:客戶端*/ public class Client {public static void main(String[] args) throws UnknownHostException, IOException {System.out.println("-----Client-----");Socket client = new Socket("localhost", 8888);// 建立鏈接new Thread(new Send(client)).start();new Thread(new Receive(client)).start();}// 發送public static class Send implements Runnable {String name;Socket client;DataOutputStream dos;BufferedReader console;DataInputStream dis;boolean isRunning = true;public Send(Socket client) {this.name = inputName();this.client = client;try {dos = new DataOutputStream(client.getOutputStream());console = new BufferedReader(new InputStreamReader(System.in));dis = new DataInputStream(client.getInputStream());} catch (Exception e) {System.out.println("構造器異常" + e);}}@Overridepublic void run() {while (isRunning) {String str = null;try {str = console.readLine();} catch (IOException e) {e.printStackTrace();}send(str);}}public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {e.printStackTrace();}return msg;}public void send(String msg) {try {dos.writeUTF(this.name + ": " + msg);dos.flush();} catch (IOException e) {e.printStackTrace();}}public void release() {new Utils().close(dis, dos, client);}public String inputName() {System.out.println("請輸入用戶名:");BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));String name = null;try {name = sc.readLine();} catch (IOException e) {e.printStackTrace();}System.out.println("歡迎: " + name+" 進入聊天室~ 你現在可以暢所欲言~");// c.close();return name;}}// 接收public static class Receive implements Runnable {Socket client;DataOutputStream dos;BufferedReader console;DataInputStream dis;boolean isRunning = true;public Receive(Socket client) {this.client = client;try {dos = new DataOutputStream(client.getOutputStream());console = new BufferedReader(new InputStreamReader(System.in));dis = new DataInputStream(client.getInputStream());} catch (Exception e) {System.out.println("構造器異常" + e);}}@Overridepublic void run() {while (isRunning) {String msg = receive();System.out.println(msg);}}public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {e.printStackTrace();}return msg;}public void release() {new Utils().close(dis, dos, client);}} }工具類
package cn.hanquan.groupchat;import java.io.Closeable;public class Utils {public void close(Closeable... targets) {for (Closeable t : targets) {try {if (null != t) {t.close();}} catch (Exception e) {e.printStackTrace();}}} }版本2: 進入提醒功能
示例
-----Client-----
請輸入用戶名:
布小谷
布小谷, Welcome ~
(系統消息) [魔鬼] 進入了聊天室
(系統消息) [逆風微笑的程序猿] 進入了聊天室
魔鬼: 我要出去了
再見哦
逆風微笑的程序猿: 再見啊
服務端
package cn.hanquan.groupchat;import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CopyOnWriteArrayList;/** 在線聊天室:服務端* 服務器不生產內容,只是轉發* * 改進:* 增加系統消息功能,新人登錄之后系統消息提示(群發時候加個bool即可)* 用戶關閉后,從容器中刪除allChannel.remove(this);并且系統發出廣播退出通知*/ public class Chat {public static CopyOnWriteArrayList<Channel> allChannel = new CopyOnWriteArrayList<>();public static void main(String[] args) throws IOException {System.out.println("-----Server-----");ServerSocket server = new ServerSocket(8888);// 指定端口 創建服務器while (true) {Socket client = server.accept();// 阻塞式等待鏈接System.out.println("A client succesfully connected.");new Thread(new Channel(client)).start();// 新線程}}// 一個Channel線程就是一個Client連接static class Channel implements Runnable {private Socket client;private DataOutputStream dos;private DataInputStream dis;public String clientName;boolean isRunning = true;public Channel(Socket client) {allChannel.add(this);this.client = client;try {this.dos = new DataOutputStream(client.getOutputStream());this.dis = new DataInputStream(client.getInputStream());} catch (IOException e) {System.out.println("構造器異常" + e);}}// 接收消息public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {isRunning = false;release();}if (msg != null) {if (msg.indexOf("(系統消息)") == 0) {this.clientName = msg.substring(msg.indexOf("[") + 1, msg.indexOf("]"));}}return msg;}// 發送消息給當前Channelpublic void send(String msg) {if (msg == null)return;try {dos.writeUTF(msg);dos.flush();} catch (IOException e) {isRunning = false;release();allChannel.remove(this);}}// 發送消息給所有Channelpublic void sendAll(String msg) {for (Channel channel : allChannel) {if (channel == this)// 不發給自己continue;channel.send(msg);}}// 釋放資源public void release() {new Utils().close(dis, dos, client);}@Overridepublic void run() {while (isRunning) {String msg = receive();sendAll(msg);}}} }客戶端
package cn.hanquan.groupchat;import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner;/** 在線聊天室:客戶端*/ public class Client {public static void main(String[] args) throws UnknownHostException, IOException {System.out.println("-----Client-----");Socket client = new Socket("localhost", 8888);// 建立鏈接new Thread(new Send(client)).start();new Thread(new Receive(client)).start();}// 發送public static class Send implements Runnable {String uname;Socket client;DataOutputStream dos;BufferedReader console;DataInputStream dis;boolean isRunning = true;// 構造器public Send(Socket client) {this.uname = inputName();this.client = client;try {dos = new DataOutputStream(client.getOutputStream());console = new BufferedReader(new InputStreamReader(System.in));dis = new DataInputStream(client.getInputStream());} catch (Exception e) {System.out.println("構造器異常" + e);}send("進入了聊天室", true);}@Overridepublic void run() {while (isRunning) {String str = null;try {str = console.readLine();} catch (IOException e) {e.printStackTrace();}send(str, false);}}// 接收public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {e.printStackTrace();}return msg;}// 發送public void send(String msg, boolean system) {try {if (system)dos.writeUTF("(系統消息) [" + this.uname + "] " + msg);elsedos.writeUTF(this.uname + ": " + msg);dos.flush();} catch (IOException e) {e.printStackTrace();}}// 釋放public void release() {new Utils().close(dis, dos, client);}public String inputName() {System.out.println("請輸入用戶名:");BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));String name = null;try {name = sc.readLine();} catch (IOException e) {e.printStackTrace();}System.out.println(name + ", Welcome ~ ");return name;}}// 接收public static class Receive implements Runnable {Socket client;DataOutputStream dos;BufferedReader console;DataInputStream dis;boolean isRunning = true;public Receive(Socket client) {this.client = client;try {dos = new DataOutputStream(client.getOutputStream());console = new BufferedReader(new InputStreamReader(System.in));dis = new DataInputStream(client.getInputStream());} catch (Exception e) {System.out.println("構造器異常" + e);}}@Overridepublic void run() {while (isRunning) {String msg = receive();System.out.println(msg);}}public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {e.printStackTrace();}return msg;}public void release() {new Utils().close(dis, dos, client);}} }工具類
版本2的工具類和版本1的工具類相同,沒有改動
package cn.hanquan.groupchat;import java.io.Closeable;public class Utils {public void close(Closeable... targets) {for (Closeable t : targets) {try {if (null != t) {t.close();}} catch (Exception e) {e.printStackTrace();}}} }運行效果
先運行一個Chat.java
再運行一個Client.java(name:魔鬼)
再運行一個Client.java(name:布小谷)
再運行一個Client.java(name:逆風微笑的程序猿)
再退出一個Client.java(name:魔鬼)
…此版本后來有改動。因為一個線程退出后,其數據不再有效,因此無法使用其類內部的sendall方法。所以取消退出提醒功能。如果想要全局提醒,可以考慮加入一個獨立的紫銅消息線程
版本3: 群聊/私聊
此版本增加了私聊功能~
規定的私聊語法:
@某人 你要說的話
是最終版了,但是里面有一些用戶可能輸入非法字符串的bug,比如輸入@@就直接string outofbound崩了,這些bug的修復,還需要進一步過濾字符串,在這里就先不改了。
服務端
package cn.hanquan.groupchat;import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CopyOnWriteArrayList;/** 在線聊天室:服務端* 服務器不生產內容,只是轉發* * 改進:* 增加系統消息功能,新人登錄之后系統消息提示(群發時候加個bool即可)* 用戶關閉后,從容器中刪除allChannel.remove(this);并且系統發出廣播退出通知*/ public class Chat {public static CopyOnWriteArrayList<Channel> allChannel = new CopyOnWriteArrayList<>();public static void main(String[] args) throws IOException {System.out.println("-----Server-----");ServerSocket server = new ServerSocket(8888);// 指定端口 創建服務器while (true) {Socket client = server.accept();// 阻塞式等待鏈接System.out.println("A client succesfully connected.");new Thread(new Channel(client)).start();// 新線程}}// 一個Channel線程就是一個Client連接static class Channel implements Runnable {private Socket client;private DataOutputStream dos;private DataInputStream dis;public String clientName;boolean isRunning = true;public Channel(Socket client) {allChannel.add(this);this.client = client;try {this.dos = new DataOutputStream(client.getOutputStream());this.dis = new DataInputStream(client.getInputStream());} catch (IOException e) {System.out.println("構造器異常" + e);}}// 接收消息public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {isRunning = false;release();}if (msg != null) {if (msg.indexOf("(系統消息)") == 0) {this.clientName = msg.substring(msg.indexOf("[") + 1, msg.indexOf("]"));}}return msg;}// 發送消息給當前Channelpublic void send(String msg) {if (msg == null)return;try {dos.writeUTF(msg);dos.flush();} catch (IOException e) {isRunning = false;release();allChannel.remove(this);}}// 發送消息給所有Channelpublic void sendAll(String msg) {try {if (msg.indexOf("@") != -1) {// 私聊格式 @SomeOne say...String toName = msg.substring(msg.indexOf("@") + 1, msg.indexOf(" ", msg.indexOf("@") + 1));for (Channel channel : allChannel) {if (channel.clientName.equals(toName)) { // 發給自己指定人channel.send(msg);break;}}} else {// 群發for (Channel channel : allChannel) {if (channel == this)// 不發給自己continue;channel.send(msg);}}} catch (NullPointerException e) {System.out.println("One client exit");isRunning = false;release();allChannel.remove(this);}}// 釋放資源public void release() {new Utils().close(dis, dos, client);}@Overridepublic void run() {while (isRunning) {String msg = receive();sendAll(msg);}}} }客戶端
package cn.hanquan.groupchat;import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner;/** 在線聊天室:客戶端*/ public class Client {public static void main(String[] args) throws UnknownHostException, IOException {System.out.println("-----Client-----");Socket client = new Socket("localhost", 8888);// 建立鏈接new Thread(new Send(client)).start();new Thread(new Receive(client)).start();}// 發送public static class Send implements Runnable {String uname;Socket client;DataOutputStream dos;BufferedReader console;DataInputStream dis;boolean isRunning = true;// 構造器public Send(Socket client) {this.uname = inputName();this.client = client;try {dos = new DataOutputStream(client.getOutputStream());console = new BufferedReader(new InputStreamReader(System.in));dis = new DataInputStream(client.getInputStream());} catch (Exception e) {System.out.println("構造器異常" + e);}send("進入了聊天室", true);}@Overridepublic void run() {while (isRunning) {String str = null;try {str = console.readLine();} catch (IOException e) {e.printStackTrace();}send(str, false);}}// 接收public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {e.printStackTrace();}return msg;}// 發送public void send(String msg, boolean system) {try {if (system)dos.writeUTF("(系統消息) [" + this.uname + "] " + msg);elsedos.writeUTF(this.uname + ": " + msg);dos.flush();} catch (IOException e) {e.printStackTrace();}}// 釋放public void release() {new Utils().close(dis, dos, client);}public String inputName() {System.out.println("請輸入用戶名:");BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));String name = null;try {name = sc.readLine();} catch (IOException e) {e.printStackTrace();}System.out.println(name + ", Welcome ~ ");return name;}}// 接收public static class Receive implements Runnable {Socket client;DataOutputStream dos;BufferedReader console;DataInputStream dis;boolean isRunning = true;public Receive(Socket client) {this.client = client;try {dos = new DataOutputStream(client.getOutputStream());console = new BufferedReader(new InputStreamReader(System.in));dis = new DataInputStream(client.getInputStream());} catch (Exception e) {System.out.println("構造器異常" + e);}}@Overridepublic void run() {while (isRunning) {String msg = receive();System.out.println(msg);}}public String receive() {String msg = null;try {msg = dis.readUTF();} catch (IOException e) {e.printStackTrace();}return msg;}public void release() {new Utils().close(dis, dos, client);}} }工具類
package cn.hanquan.groupchat;import java.io.Closeable;public class Utils {public void close(Closeable... targets) {for (Closeable t : targets) {try {if (null != t) {t.close();}} catch (Exception e) {e.printStackTrace();}}} }運行效果
總結
以上是生活随笔為你收集整理的【Java网络编程(四)】手写TCP聊天室——控制台版的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Java网络编程(三)】TCP的使用—
- 下一篇: java美元兑换,(Java实现) 美元