|
|
|
|
@ -1,11 +1,14 @@
|
|
|
|
|
package server;
|
|
|
|
|
|
|
|
|
|
import common.Message;
|
|
|
|
|
import common.MessageType;
|
|
|
|
|
import java.io.*;
|
|
|
|
|
import java.net.*;
|
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
|
import java.security.MessageDigest;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
|
import java.util.regex.*;
|
|
|
|
|
import com.google.gson.Gson;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -29,7 +32,6 @@ public class WebSocketServer {
|
|
|
|
|
public void start() {
|
|
|
|
|
try {
|
|
|
|
|
serverSocket = new ServerSocket(PORT);
|
|
|
|
|
serverSocket.setReuseAddress(true);
|
|
|
|
|
running = true;
|
|
|
|
|
System.out.println("WebSocket服务器启动成功,监听端口: " + PORT);
|
|
|
|
|
System.out.println("Web客户端访问地址: http://localhost:" + PORT);
|
|
|
|
|
@ -38,9 +40,6 @@ public class WebSocketServer {
|
|
|
|
|
// 检查web文件是否存在
|
|
|
|
|
checkWebFiles();
|
|
|
|
|
|
|
|
|
|
// 启动心跳检测线程
|
|
|
|
|
startHeartbeatCheck();
|
|
|
|
|
|
|
|
|
|
while (running) {
|
|
|
|
|
try {
|
|
|
|
|
Socket clientSocket = serverSocket.accept();
|
|
|
|
|
@ -57,42 +56,6 @@ public class WebSocketServer {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startHeartbeatCheck() {
|
|
|
|
|
Thread heartbeatThread = new Thread(() -> {
|
|
|
|
|
while (running) {
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(30000); // 每30秒检查一次
|
|
|
|
|
|
|
|
|
|
Map<String, Object> pingMessage = new HashMap<>();
|
|
|
|
|
pingMessage.put("type", "PING");
|
|
|
|
|
pingMessage.put("timestamp", System.currentTimeMillis());
|
|
|
|
|
String pingJson = gson.toJson(pingMessage);
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
List<String> deadClients = new ArrayList<>();
|
|
|
|
|
for (Map.Entry<String, WebSocketClient> entry : clients.entrySet()) {
|
|
|
|
|
try {
|
|
|
|
|
entry.getValue().sendMessage(pingJson);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
System.err.println("发送心跳包失败: " + entry.getKey() + ", " + e.getMessage());
|
|
|
|
|
deadClients.add(entry.getKey());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 清理死亡的客户端
|
|
|
|
|
for (String username : deadClients) {
|
|
|
|
|
removeClient(username);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
heartbeatThread.setDaemon(true);
|
|
|
|
|
heartbeatThread.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleClient(Socket socket) {
|
|
|
|
|
try {
|
|
|
|
|
BufferedReader reader = new BufferedReader(
|
|
|
|
|
@ -283,11 +246,9 @@ public class WebSocketServer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public synchronized void removeClient(String username) {
|
|
|
|
|
WebSocketClient client = clients.remove(username);
|
|
|
|
|
if (client != null) {
|
|
|
|
|
System.out.println("用户 " + username + " 已离线,当前在线: " + clients.size());
|
|
|
|
|
broadcastUserList();
|
|
|
|
|
}
|
|
|
|
|
clients.remove(username);
|
|
|
|
|
System.out.println("用户 " + username + " 已离线,当前在线: " + clients.size());
|
|
|
|
|
broadcastUserList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void sendPrivateMessage(String sender, String receiver, String content) {
|
|
|
|
|
@ -331,10 +292,10 @@ public class WebSocketServer {
|
|
|
|
|
System.out.println("找到接收者客户端,准备发送");
|
|
|
|
|
String jsonMessage = gson.toJson(messageData);
|
|
|
|
|
int messageSize = jsonMessage.length();
|
|
|
|
|
System.out.println("消息大小: " + messageSize + " 字符 (" + formatSize(messageSize) + ")");
|
|
|
|
|
System.out.println("消息大小: " + messageSize + " 字符");
|
|
|
|
|
|
|
|
|
|
// 提高消息大小限制到5MB
|
|
|
|
|
if (messageSize > 5 * 1024 * 1024) { // 5MB
|
|
|
|
|
// 如果消息太大(超过100KB),建议压缩或分片
|
|
|
|
|
if (messageSize > 100000) {
|
|
|
|
|
System.out.println("警告:消息过大,可能导致传输问题");
|
|
|
|
|
|
|
|
|
|
// 发送错误消息给发送者
|
|
|
|
|
@ -347,27 +308,8 @@ public class WebSocketServer {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
receiverClient.sendMessage(jsonMessage);
|
|
|
|
|
System.out.println("消息已发送到接收者");
|
|
|
|
|
|
|
|
|
|
// 发送成功确认给发送者
|
|
|
|
|
if (senderClient != null) {
|
|
|
|
|
Map<String, Object> ack = new HashMap<>();
|
|
|
|
|
ack.put("type", "SEND_SUCCESS");
|
|
|
|
|
ack.put("receiver", receiver);
|
|
|
|
|
ack.put("timestamp", System.currentTimeMillis());
|
|
|
|
|
senderClient.sendMessage(gson.toJson(ack));
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
System.err.println("发送消息失败: " + e.getMessage());
|
|
|
|
|
if (senderClient != null) {
|
|
|
|
|
Map<String, Object> error = new HashMap<>();
|
|
|
|
|
error.put("type", "ERROR");
|
|
|
|
|
error.put("content", "发送失败: " + e.getMessage());
|
|
|
|
|
senderClient.sendMessage(gson.toJson(error));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
receiverClient.sendMessage(jsonMessage);
|
|
|
|
|
System.out.println("消息已发送到接收者");
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("接收者不在线: " + receiver);
|
|
|
|
|
if (senderClient != null) {
|
|
|
|
|
@ -423,34 +365,12 @@ public class WebSocketServer {
|
|
|
|
|
System.out.println();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void stop() {
|
|
|
|
|
running = false;
|
|
|
|
|
try {
|
|
|
|
|
if (serverSocket != null && !serverSocket.isClosed()) {
|
|
|
|
|
serverSocket.close();
|
|
|
|
|
}
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
System.err.println("关闭服务器失败: " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
threadPool.shutdown();
|
|
|
|
|
try {
|
|
|
|
|
if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
|
|
|
|
|
threadPool.shutdownNow();
|
|
|
|
|
}
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
threadPool.shutdownNow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
System.out.println("服务器已停止");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
WebSocketServer server = new WebSocketServer();
|
|
|
|
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
|
|
|
System.out.println("\n正在关闭服务器...");
|
|
|
|
|
server.stop();
|
|
|
|
|
}));
|
|
|
|
|
server.start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|