Java基于TCP实现服务端向客户端消息群发及断线重连
在需要与服务端保持长连接并进行双向或广播通信的场景中,TCP协议是基础选择。本文提供一套完整的Java TCP Demo,涵盖服务端建立连接池、向在线客户端群发消息、检测并清理断开连接的Socket,以及客户端检测到断线后自动重连的完整流程。
核心原理与目标
TCP连接是非持久的,网络波动或服务重启会导致连接断开。为了实现稳定的消息群发,系统需要解决两个核心问题:
- 连接管理:服务端需维护一个活跃客户端列表(List of Socket),并在群发时遍历发送。
- 状态探测与清理:直接判断Socket是否关闭较难,通常通过发送测试数据或探测信号来判断连接活性,及时移除“僵尸”连接以防止报错。
- 断线重连:当客户端感知到连接断开时,应自动尝试重新建立连接,以保持业务的连续性。
准备工作
你需要一个支持Java 8及以上版本的开发环境(如IDEA或Eclipse)。本示例包含四个主要类:HttpSocketServer(主服务入口)、ServerThread(单连接处理线程)、TcpTool(连接管理与工具类)和 HttpSocketClient(客户端程序)。
真实操作顺序
第1步:构建服务端主进程
目的:监听指定端口,接受客户端连接请求并为每个新连接分配独立线程处理。
关键代码:使用 ServerSocket 绑定端口 9020,调用 accept() 阻塞等待连接,并将获取的 Socket 实例交给 ServerThread。
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpSocketServer {
public static void main(String[] args) {
try {
// 1. 初始化服务端Socket,监听9020端口
ServerSocket server = new ServerSocket(9020);
System.out.println("Server started on port 9020");
// 2. 循环Accept客户端连接
while (true) {
Socket client = server.accept();
System.out.println("New client connected: " + client.getRemoteSocketAddress());
// 3. 为每个客户端创建独立线程
ServerThread st = new ServerThread(client);
st.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
第2步:实现连接加入与生命周期管理
目的:将新建立的客户端Socket加入全局列表,供后续群发使用。
动作说明:ServerThread 继承自 Thread,在构造函数中接收Socket,并在 run() 方法中调用 TcpTool.addSocket() 将连接注册到管理器中。
import java.io.IOException;
import java.net.Socket;
public class ServerThread extends Thread {
private Socket client;
public ServerThread(Socket client) {
this.client = client;
}
@Override
public void run() {
try {
// 将该客户端加入群发列表
TcpTool.addSocket(client);
// 此处可扩展:读取客户端消息或维持心跳的逻辑
} catch (Exception e) {
e.printStackTrace();
// 发生异常时,通常意味着连接断开,从列表中移除
TcpTool.delSocket(client);
}
}
}
第3步:封装群发与连接检测工具类
目的:提供一个静态工具类,集中处理Socket列表的增删查改以及“活体检测”逻辑。
连接检测原理:利用 Socket.sendUrgentData(0xFF) 发送带外数据。如果连接已断开,此操作会抛出异常,从而准确识别失效连接并予以移除。
操作细节:
- addSocket:同步添加Socket到ArrayList。
- sendAll:遍历列表,先检测有效性,有效则写入数据并Flush;无效则删除。
- clientIsClose:通过捕获异常判断连接状态。
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class TcpTool {
// 使用volatile或Collections.synchronizedList可增强并发安全性,本Demo为简化展示使用普通List
private static List<Socket> clientList = new ArrayList<>();
public synchronized static void addSocket(Socket socket) {
clientList.add(socket);
}
public synchronized static void delSocket(Socket socket) {
clientList.remove(socket);
}
/**
* 向所有在线客户端群发消息
*/
public static void sendAll(String msg) {
if (msg == null) return;
// 倒序遍历或迭代器模式更安全,此处演示简单索引方式
for (int i = 0; i < clientList.size(); i++) {
Socket client = clientList.get(i);
// 1. 检测连接是否存活
if (clientIsClosed(client)) {
delSocket(client); // 移除死链
i--; // 修正索引,因为当前元素被移除了
continue;
}
// 2. 写入数据
try {
OutputStream ops = client.getOutputStream();
ops.write((msg + "\r\n").getBytes());
ops.flush();
} catch (IOException e) {
// 写入失败也可能导致连接断开
delSocket(client);
e.printStackTrace();
}
}
}
/**
* 检测Socket是否处于关闭/失效状态
*/
public static boolean clientIsClosed(Socket socket) {
try {
// 发送1个字节的紧急数据
socket.sendUrgentData(0xFF);
return false; // 无异常,说明连接正常
} catch (Exception e) {
return true; // 有异常,说明连接已断
}
}
}
第4步:实现客户端断线重连逻辑
目的:客户端持续监听服务端消息,一旦检测到连接断开或读写异常,自动断开旧连接并尝试在5秒后重连。
操作流程:
- 建立初始连接
new Socket("127.0.0.1", 9020)。 - 进入
while(true)循环。 - 每次循环前检查连接状态,若断开则休眠后重建。
- 使用
BufferedReader读取行数据,打印消息。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class HttpSocketClient {
public static void main(String[] args) throws IOException {
Socket client = null;
while (true) {
try {
// 如果客户端为空或连接已关闭,则尝试重连
if (client == null || clientIsClosed(client)) {
if (client != null) {
client.close(); // 关闭旧流
}
System.out.println("Attempting to reconnect...");
Thread.sleep(5000); // 等待5秒后重连,避免频繁尝试占用资源
client = new Socket("127.0.0.1", 9020);
System.out.println("Reconnected successfully.");
}
// 执行读取操作
InputStream is = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String text = reader.readLine();
if (text != null && !text.isEmpty()) {
System.out.println("From Server: " + text);
}
} catch (IOException e) {
System.err.println("Connection lost or error: " + e.getMessage());
client = null; // 标记连接失效,下次循环进入重连逻辑
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 复用与服务端相同的检测逻辑
private static boolean clientIsClosed(Socket socket) {
try {
socket.sendUrgentData(0xFF);
return false;
} catch (Exception e) {
return true;
}
}
}
运行测试与结果验证
按照以下顺序启动程序以验证功能:
- 首先运行
HttpSocketServer,控制台显示Server started...。 - 开启多个终端窗口,分别运行
HttpSocketClient,模拟多客户端接入。 - 在服务端代码中的
TcpTool.sendAll()处添加定时任务或手动触发调用(例如在其他线程中调用TcpTool.sendAll("Hello All"))。 - 观察所有客户端是否同时收到相同的消息。
当你在某个客户端终端强制关闭该进程,或模拟网络中断时,服务端下一次群发时会自动通过 clientIsClosed 检测并移除该失效Socket,不影响其他在线客户端。而被关闭的客户端再次启动时,将按照逻辑重新发起连接请求。
上述代码展示了最基础的TCP长连接群发与重连机制。在实际生产环境中,建议引入Netty等NIO框架以提升高并发性能,并使用更复杂的握手协议和心跳包(Heartbeat)来替代简单的紧急数据探测,以保证连接的健壮性和实时性。
以上就是基于Java TCP实现服务端向客户端消息群发及断线重连的详细技术解析,更多关于Java网络编程与Socket通信的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













