对象传输不能实现轮询,因其依赖长连接而轮询需频繁建连;正确做法是复用单连接,通过主动心跳、显式请求响应配对及超时控制模拟轮询节奏。

对象传输本身不支持轮询——轮询是客户端周期性建连、发请求、关连的无状态行为,而对象传输(如 Java 的 ObjectInputStream/ObjectOutputStream)依赖已建立的 TCP 长连接,用于持续双向序列化通信。因此,“用对象传输实现轮询”在语义上存在混淆。但如果你是在简单练习中想**借助对象序列化能力,模拟出类似轮询的可控、可观察、易理解的请求-响应节奏**,那实际做法是:**放弃轮询逻辑,改用单次长连接 + 主动心跳 + 显式请求/响应配对**,并严格控制资源生命周期。
为什么不能真用对象流做轮询
每次轮询都新建 Socket 并包装对象流,会导致:
- 反复序列化头字节(Stream Header),极易触发
StreamCorruptedException - 频繁创建/销毁对象流开销大,违背练习初衷
- 服务端无法区分“新连接”和“重连”,状态难管理
- 客户端无法保证每次 connect 后立即 write,服务端 read 可能阻塞或错序
更合理的学习路径:对象流 + 伪轮询节奏
保留对象流的便利性(传对象而非字节),但用流程控制模拟轮询感——即:客户端主动发起请求、等待响应、处理结果、停顿再发下一轮。整个过程复用同一连接。
- 客户端启动时建立一次连接,初始化一对对象流,并发送握手对象(如
new Handshake("CLIENT", 1))完成通道校验 - 主业务循环为:写请求对象 → 调用
flush()→ 设定读超时 → 读响应对象 → 处理 →Thread.sleep(3000) - 服务端收到请求后立即处理,构造响应对象写出,不关闭 Socket,也不关闭流,等待下一次读
- 双方均通过
socket.setSoTimeout(5000)防止永久阻塞;捕获SocketTimeoutException后继续循环
关键实操细节
要让这种模式稳定运行,必须注意:
- 所有传输类(如
StatusRequest、StatusResponse)必须实现Serializable,且显式声明serialVersionUID,两端完全一致 - 客户端每次
writeObject()后必须调用flush(),否则服务端readObject()可能卡住 - 避免在对象流上混用原始流操作(如
socket.getOutputStream().write()),会破坏序列化协议 - 异常处理要覆盖
IOException、ClassNotFoundException、InvalidClassException,失败时不退出循环,仅记录并 sleep 后重试
什么时候该切换方案
如果出现以下情况,说明已超出该模式适用范围,应转向更合适的模型:
- 需要服务端不等请求就主动推送(换长轮询或 WebSocket)
- 并发客户端数超过 10 个(换 NIO + 线程池或 Netty)
- 要求毫秒级响应或低延迟(换无序列化开销的二进制协议)
- 频繁发生
StreamCorruptedException或连接意外中断(检查网络、防火墙、超时设置)

















