功能演示
【基于TCP的Socket聊天Demo】 https://www.bilibili.com/video/BV1yd4y1r7tJ/?share_source=copy_web&vd_source=603d76094f4b03d34ae4f468d5e77227
实现原理
每个客户端为其新建一个服务端线程, 一个客户端对应一个服务端线程。
私发: 通过服务端转发到目标客户端
群发:通过服务端向所有客户端转发
代码
客户端
import java.io.IOException; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; /** * @Author Tiam * @Date 2022/11/4 21:06 * @Description: */ public class Client1 { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 8888); System.out.println(socket); // 开启读取消息的线程, 使其可以一直读取消息 new Thread(new ClientThread(socket)).start(); // 输出流 用于向服务端发送消息 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 输入流 Scanner scanner = new Scanner(System.in); while (true) { // System.out.print("请输入:"); String message = scanner.nextLine(); out.println(message); } } catch (IOException e) { e.printStackTrace(); } } }
读消息线程
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; /** * @Author Tiam * @Date 2022/11/6 22:06 * @Description: 读取消息 */ public class ClientThread implements Runnable { Socket socket; public ClientThread(Socket socket) { this.socket = socket; } @Override public void run() { boolean isExit = true; while (isExit) { try { readMessage(); } catch (Exception e) { System.out.println("服务器断开连接"); isExit = false; System.exit(-1); } } } /** * 线程: 读取其他用户的消息 */ private void readMessage() throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println("\n正在等待返回消息... "); String message = in.readLine(); System.out.println(message); } }
服务端
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; /** * @Author Tiam * @Date 2022/11/4 21:10 * @Description: 服务端启动类 */ public class RunServer { public static Map<Socket, String> map = new HashMap<>(); public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; try { serverSocket = new ServerSocket(8888); System.out.println("服务端已启动, 等待连接..."); int num = 0; while (true) { socket = serverSocket.accept(); map.put(socket, null); System.out.println(socket+"已连接, "+(++num)+"号用户"); new Thread(new ServerThread(socket)).start(); } } catch (IOException e) { e.printStackTrace(); } } }
服务端线程
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; /** * @Author Tiam * @Date 2022/11/4 21:10 * @Description: */ public class ServerThread implements Runnable { private Socket socket; public ServerThread(Socket socket) { this.socket = socket; } @Override public void run() { while (true) { send(); } } private void send() { try { BufferedReader in = null; PrintWriter out = null; in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String mes = null; try { mes = in.readLine(); } catch (IOException e) { System.out.println("客户端异常关闭"); e.printStackTrace(); System.exit(-1); } // 判断是否是否为私发, 截取端口号5位 String to = null; if (mes.length() > 5) { to = mes.substring(0, 5); // 找到端口目标端口 to, 向其发送消息后停止执行 for (Socket socket1 : RunServer.map.keySet()) { if (to.equals(String.valueOf(socket1.getPort()))) { out = new PrintWriter(socket1.getOutputStream(), true); out.println(mes.substring(5)); return; } } } // 如未找到目标端口, 按群发消息处理, 转发给所有在线的客户端 String message = "【群发消息】" + socket.getPort() + ":" + mes; System.out.println(message); // 将某个客户端发送过来的消息, 转发给所有在线用户 for (Socket socket1 : RunServer.map.keySet()) { // 跳过自己 if (socket1 == this.socket) continue; out = new PrintWriter(socket1.getOutputStream(), true); out.println(message); } } catch (IOException e) { e.printStackTrace(); } } }
如何同时运行多个客户端?