3. HSMS 连接建立:主动连接与被动连接模式、T5 超时与连接重试机制、连接状态机
好,咱们今天聊点实在的——HSMS 连接到底怎么建起来的?
很多刚接触半导体设备通信的工程师,上来就照着协议文档配 IP 和端口,结果连不上就开始抓瞎。其实 HSMS 的连接建立并不复杂,但有几个关键点你必须吃透:主动 vs 被动模式、T5 超时、还有那个状态机。搞懂了这些,你调试的时候至少能少掉一半头发。
3.1 主动连接与被动连接模式
HSMS 的连接模式说白了就两种:谁先发起 TCP 连接。
- 主动模式(Active):本端主动去连对端的 TCP 端口。通常用于设备端去连主机端。
- 被动模式(Passive):本端监听一个 TCP 端口,等对端来连。通常用于主机端等待设备连接。
我个人习惯把主动模式叫「打电话」,被动模式叫「等电话」。你想想看,打电话的人得知道对方号码(IP+Port),等电话的人只需要守着座机就行。
关键点:一个 HSMS 实体只能选一种模式。不能又主动又被动——除非你用 Select 模式,但那属于进阶玩法,后面章节会讲。
我在项目中遇到过一种情况:设备端配了主动模式,但主机端防火墙没开端口。结果设备端一直在重试,日志里全是 "Connection refused"。嗯,这种问题其实很好排查——先确认对端端口是不是真的在监听。
模式选择建议
| 场景 | 推荐模式 | 说明 |
|---|---|---|
| 设备 → 主机 | 主动 | 设备知道主机 IP,主动发起连接 |
| 主机 → 设备 | 被动 | 主机监听,等待设备连接 |
| 双机互备 | Select 模式 | 后续章节详解 |
3.2 T5 超时与连接重试机制
T5 超时是 HSMS 里最容易踩坑的参数之一。它的定义很简单:从发起 TCP 连接到收到 Select.req 响应的最大等待时间。
但实际项目中,T5 超时往往被误解。我曾经见过一个团队把 T5 设成 2 秒,结果设备频繁断连。为什么?因为 TCP 握手 + Select 交互在网络上跑一圈可能就要 1.5 秒,稍微有点抖动就超时了。
避坑指南:T5 的默认值是 10 秒,我个人建议不要低于 5 秒。除非你在同一个子网内,延迟极低。
连接重试机制是这样的:
- 发起 TCP 连接
- 等待 T5 超时
- 如果超时未收到 Select.req,关闭 TCP 连接
- 等待重试间隔(通常由应用层控制)
- 重新发起 TCP 连接
说白了,这就是一个「连不上就重试」的循环。但要注意——重试不能无限进行。SEMI 标准建议最多重试 3 次,之后应该上报错误。
小技巧:重试间隔建议用指数退避。第一次等 1 秒,第二次等 2 秒,第三次等 4 秒。这样既不会把对端冲垮,也不会在短暂故障时错过恢复窗口。
3.3 连接状态机
HSMS 的连接状态机是理解整个通信过程的核心。我刚开始学的时候觉得状态机很抽象,后来画了一张图,一下子就通了。
这张图我建议你保存下来。调试的时候对照着看,能快速定位当前卡在哪个环节。
状态转换详解
- NOT CONNECTED → CONNECTED:TCP 三次握手成功。此时还不能发业务消息,只能发 Select.req。
- CONNECTED → SELECTED:收到 Select.rsp 且状态为成功。这时候才算真正「连上了」。
- CONNECTED → NOT CONNECTED:T5 超时,或者对端主动断开。
- SELECTED → NOT CONNECTED:连接异常断开,比如网线被拔了。
重点:很多工程师以为 TCP 连上就等于 HSMS 连上了。不对。TCP 连上只是第一步,必须完成 Select 握手,状态机走到 SELECTED,才算真正建立 HSMS 连接。
3.4 实战:连接建立代码示例
下面我用 Python 写一个简单的主动模式连接示例。注意看 T5 超时的处理逻辑。
import socket
import time
class HSMSConnection:
def __init__(self, host, port, t5_timeout=10):
self.host = host
self.port = port
self.t5_timeout = t5_timeout
self.sock = None
self.state = "NOT_CONNECTED"
def connect(self):
"""主动连接并完成 Select 握手"""
try:
# 1. TCP 连接
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.t5_timeout)
self.sock.connect((self.host, self.port))
self.state = "CONNECTED"
print(f"[INFO] TCP 连接建立: {self.host}:{self.port}")
# 2. 发送 Select.req
self._send_select_req()
# 3. 等待 Select.rsp(受 T5 约束)
rsp = self._recv_select_rsp()
if rsp and rsp["status"] == 0:
self.state = "SELECTED"
print("[INFO] HSMS 连接建立成功")
return True
else:
raise Exception("Select 握手失败")
except socket.timeout:
print(f"[ERROR] T5 超时 ({self.t5_timeout}s)")
self.disconnect()
return False
except Exception as e:
print(f"[ERROR] 连接失败: {e}")
self.disconnect()
return False
def disconnect(self):
"""断开连接"""
if self.sock:
self.sock.close()
self.sock = None
self.state = "NOT_CONNECTED"
print("[INFO] 连接已断开")
def _send_select_req(self):
# 构造 Select.req 消息(简化)
pass
def _recv_select_rsp(self):
# 接收并解析 Select.rsp
pass
个人经验:我在实际项目中,会在 connect() 外面包一层重试逻辑。比如重试 3 次,每次间隔指数退避。这样网络抖动时不会直接报错,而是自动恢复。
3.5 常见问题与排查思路
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| TCP 连不上 | IP/Port 错误、防火墙拦截 | telnet 测试、抓包 |
| TCP 连上但 Select 超时 | T5 设置过短、对端未响应 | 检查 T5 配置、抓包看 Select.req 是否发出 |
| Select 握手失败 | 设备 ID 冲突、协议版本不匹配 | 检查 Select.rsp 中的错误码 |
| 连接频繁断开 | T5 超时、网络不稳定 | 增大 T5、检查网络质量 |
嗯,这一章的内容差不多就这些。记住三个核心:模式选对、T5 别设太短、状态机盯死。下次调试 HSMS 连接的时候,先问自己三个问题:我现在在哪个状态?T5 还剩多少秒?对端有没有在监听?