功能演示
【基于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();
}
}
}