Two Hosts, One HSMS Port: Why the First Session Never Finds Out
A capture of two host connections into one passive HSMS listener. Both get Select Status 0, and the first is never told the second arrived.
The host log looks clean. Linktest keeps coming back, the session says SELECTED. Meanwhile the tool's screen reads NOT CONNECTED and no event report has arrived in half an hour. The only thing that happened in between: an engineer at the next desk opened a laptop, connected to the same port, and closed it again.
This is a capture of Host A and Host B connected at the same time to one passive HSMS listener. Both sent Select.req, both got a Select.rsp carrying Select Status 0. When Host B sent Separate.req and left, the equipment's session state dropped to NOT CONNECTED — and Host A's Linktest.req kept getting a Linktest.rsp afterwards. A never finds out.
The listener did not refuse the second connection
Host A connects first. In the hex below the leading four bytes are the length header; the ten after it are what SEMI E37 calls Header Byte 0–9.
TCP connect → 127.0.0.1:5501
TX 00 00 00 0A | 00 0B 00 00 00 01 00 00 0A 01 Select.req
RX 00 00 00 0A | 00 0B 00 00 00 02 00 00 0A 01 Select.rsp
length 00 00 00 0A ten header bytes follow
Byte 0–1 00 0B SessionID = 11
Byte 2 00 zero on a control message
Byte 3 00 on Select.rsp this is Select Status = 0, accepted
Byte 4 00 PType = SECS-II
Byte 5 01 → 02 SType, Select.req → Select.rsp
Byte 6–9 00 00 0A 01 SystemBytes, echoed back unchanged
Textbook so far. The equipment's session state is now SELECTED.
Now Host B connects to the same port. A new TCP connection, with A's socket still open.
TCP connect → 127.0.0.1:5501 (second socket)
TX 00 00 00 0A | 00 0B 00 00 00 01 00 00 0B 01 Select.req
RX 00 00 00 0A | 00 0B 00 00 00 02 00 00 0B 01 Select.rsp
Same reply A got, except the SystemBytes are 00 00 0B 01. Byte 3 is 00 — accepted, not refused. Two hosts are now simultaneously SELECTED against one tool.
The first host gets no signal at all
Right after B arrives, A sends a keepalive.
TX 00 00 00 0A | 00 0B 00 00 00 05 00 00 0A 03 Linktest.req (Host A)
RX 00 00 00 0A | 00 0B 00 00 00 06 00 00 0A 03 Linktest.rsp
SType 05 → 06, SystemBytes 00 00 0A 03 echoed. Nothing has changed from A's point of view.
If your host judges session health by Linktest alone — most do — there is nothing here to find. A Linktest round-trip means the socket is alive. It does not mean your session is the one the equipment recognises. That is the mirror image of a tool that answers Linktest but never completes Select.
B leaves, and takes A's session state with it
B sends one Separate.req and closes the socket.
TX 00 00 00 0A | 00 0B 00 00 00 09 00 00 0B 02 Separate.req (Host B)
(equipment closes B's socket)
Immediately after, the equipment's session state is NOT CONNECTED. A's socket is still open. 300 ms later A sends another Linktest.
TX 00 00 00 0A | 00 0B 00 00 00 05 00 00 0A 04 Linktest.req (Host A)
RX 00 00 00 0A | 00 0B 00 00 00 06 00 00 0A 04 Linktest.rsp
It answers. The equipment believes there is no session, and the keepalive keeps circulating.
That happens because this listener keeps one session state per tool rather than one per connection. Whether that is specific to this implementation doesn't matter much from the host side. What matters is that from the moment the second connection arrived, A's view and the equipment's view diverged, and A has no way inside the protocol to discover it. A good share of the session incidents that end up filed as cause unknown look exactly like this.
Where the standard draws the line
E37's connection state machine — NOT CONNECTED / CONNECTED, with NOT SELECTED and SELECTED underneath CONNECTED — is defined against a single TCP connection. What GEM equipment actually runs is the single-session subsidiary standard, E37.1 (HSMS-SS), and the name is the point: one session.
So the second Select.req should be refused, and the place to refuse it already exists — a non-zero Select Status in Byte 3 of Select.rsp. Which code corresponds to "a session is already active" is in E37's table and in the tool's manual; I have not verified that value against a tool. What I verified is that this listener returned 00, and 00 is not a refusal.
Which leaves the host meeting two kinds of equipment: the kind that refuses a second Select, and the kind that quietly accepts it. There is no way to know in advance which one you have, and if it's the second kind the symptom is the capture above.
What to do on the host side
Not being the second connection is the only defence that actually holds. An EAP or engineering tool sharing an equipment port with the MES host is something to catch at design time. It comes back later as "session drops, cause unknown", and by then there is nothing in the log.
The cheapest fix is at the firewall: pin the source addresses allowed to reach the equipment port to exactly one host. What the protocol can't prevent, the network can. This isn't a preference — HSMS has no authentication, and anything that can reach the port can send Select.req.
Two things on the host stack itself.
- Don't use Linktest as proof of a session. It is proof of liveness. Periodically sending one data message with the W-bit set and checking for the reply tells you far more.
- Log the peer address on connection events. If you can see the equipment's connection log, the first question is "did another IP connect at that time?" Too many host stacks never record it, so there's nothing to answer with when it matters.
Telling a clean Separate.req teardown from somebody else ending your session for you comes down to lining up timestamps from both logs.
Reproducing it
Every capture came from pointing two real sockets at the EQ1 passive listener (port 5501) in the SECS/GEM simulator. The equipment side logged two TCP connections and an RX for every frame, which is how I know these bytes were on the wire. The raw export is in content/demos/hsms-two-hosts-one-listener.json.
The sequence, if you want to run it: connect socket A and select, leave A open, connect socket B to the same port and select, then send a Linktest on socket A. The point of the exercise is finding out what your own host stack logs when this happens — most likely nothing.