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 秒。除非你在同一个子网内,延迟极低。

连接重试机制是这样的:

  1. 发起 TCP 连接
  2. 等待 T5 超时
  3. 如果超时未收到 Select.req,关闭 TCP 连接
  4. 等待重试间隔(通常由应用层控制)
  5. 重新发起 TCP 连接

说白了,这就是一个「连不上就重试」的循环。但要注意——重试不能无限进行。SEMI 标准建议最多重试 3 次,之后应该上报错误。

小技巧:重试间隔建议用指数退避。第一次等 1 秒,第二次等 2 秒,第三次等 4 秒。这样既不会把对端冲垮,也不会在短暂故障时错过恢复窗口。

3.3 连接状态机

HSMS 的连接状态机是理解整个通信过程的核心。我刚开始学的时候觉得状态机很抽象,后来画了一张图,一下子就通了。

HSMS 连接状态机 HSMS 连接状态机 NOT CONNECTED CONNECTED SELECTED T5 TIMEOUT TCP 连接建立 Select.req/rsp T5 超时 关闭 TCP 连接 异常断开 状态说明: • NOT CONNECTED:初始状态,无 TCP 连接 • CONNECTED:TCP 已建立,但未完成 Select 握手 • SELECTED:Select 握手完成,可以收发消息

这张图我建议你保存下来。调试的时候对照着看,能快速定位当前卡在哪个环节。

状态转换详解

  • 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 还剩多少秒?对端有没有在监听?