← Articles
SECS/GEM/7 min read/ views

Why Your HSMS Separate.req Never Gets a Reply, and What Happens If You Skip It

The host shut down but the tool still shows SELECTED. Two captures side by side: a session ended with Separate.req, and one where only the socket closed.

SECS/GEMMESSCADATroubleshootingChecklists

Some sites hit this every time the host restarts: the tool is still holding the previous session. The host log says "connection closed", the equipment screen still says SELECTED. Neither is lying. The host never ended the session — it only closed the socket.

HSMS has a dedicated message for ending a session: Separate.req. The two captures below were taken against the same tool over a real socket, one using it and one not. In the clean teardown the Host sends Select.req and Linktest.req, gets Select.rsp and Linktest.rsp back, then sends Separate.req — and that one gets no reply, followed immediately by a TCP close.

HSMS session teardown: Select.req/Select.rsp and Linktest.req/Linktest.rsp are paired, but Separate.req gets no reply and is followed by a TCP close HostEquipment Select.req Select.rsp Linktest.req Linktest.rsp Separate.req no reply TCP close

What a clean teardown looks like

I opened a socket in the host role and sent three messages in order. Only what the equipment side logged as RX is reproduced here.

TX Select.req     00 00 00 0A 00 0B 00 00 00 01 00 00 02 01
RX Select.rsp     00 00 00 0A 00 0B 00 00 00 02 00 00 02 01
TX Linktest.req   00 00 00 0A 00 0B 00 00 00 05 00 00 02 02
RX Linktest.rsp   00 00 00 0A 00 0B 00 00 00 06 00 00 02 02
TX Separate.req   00 00 00 0A 00 0B 00 00 00 09 00 00 02 03
RX (nothing)      the equipment closed the TCP connection

The header reads the same way it always does. 00 00 00 0A says ten bytes follow, and control messages carry no body so it's always ten. 00 0B is the SessionID (11), the next two bytes go unused for control messages, 00 is the PType, and the byte after that is the SType. The last four bytes are the SystemBytes.

Only the SType is interesting here. 01 is Select.req, 02 Select.rsp, 05 Linktest.req, 06 Linktest.rsp, and 09 is Separate.req. The first two pairs echo their SystemBytes back — 00 00 02 01 answered by 00 00 02 01, 00 00 02 02 by 00 00 02 02. The third got nothing at all; the socket went to EOF instead.

The silence is correct

SEMI E37 assigns SType 0 to data messages, and the control messages are Select.req (1), Select.rsp (2), Deselect.req (3), Deselect.rsp (4), Linktest.req (5), Linktest.rsp (6), Reject.req (7), and Separate.req (9). Run down the list and the pattern shows itself: every .req has a matching .rsp except Reject.req and Separate.req.

So Select and Linktest are request/response transactions, and a missing answer to either trips T6, the control transaction timeout. Separate.req isn't a transaction. The sender considers the session over the moment it writes those bytes, and the receiver drops to NOT SELECTED without acknowledging anything.

The place this actually bites is host code. Write the shutdown path with the same helper as the other control messages — send, await reply, raise on timeout — and every normal shutdown now ends in a timeout exception. A separate failed line accumulates in the log on every disconnect and nobody realises that is the protocol working. Sending Separate.req and closing the socket is the whole procedure.

After the capture I pushed one more Linktest down the same socket.

TX Linktest.req   00 00 00 0A 00 0B 00 00 00 05 00 00 02 04
RX (nothing)

The connection was already gone, so of course nothing came back. If your host is still sending after Separate, the state machine isn't following the transition.

What happens if you only close the socket

I reconnected to the same tool, selected, and dropped the socket without a Separate. SO_LINGER set to 0 so a RST went out instead of a FIN — close to what a host process dying looks like.

TX Select.req     00 00 00 0A 00 0B 00 00 00 01 00 00 03 01
RX Select.rsp     00 00 00 0A 00 0B 00 00 00 02 00 00 03 01
   (socket closed — no HSMS message was sent)

The equipment logged read ECONNRESET and dropped straight to NOT CONNECTED. This case is fine, as it happens. TCP did the telling for you.

The trouble is when TCP can't tell anyone. Pull power on the host server, or let a firewall in the middle silently drop the session, and no FIN and no RST is ever sent. The equipment stays SELECTED, and when the host reconnects the tool believes it has two sessions. That is what "it won't connect after a restart" usually turns out to be. I could not produce that state as a capture here — on loopback the kernel always sends the RST for you.

Which is why Linktest exists. Whether the far end is alive is something the application has to keep asking, and TCP's silence is not an answer. The opposite case — the keepalive passes but the session never forms — is in A Tool That Answers Linktest But Never Completes Select.

Don't reconnect immediately

E37 defines T5, the connect separation timeout: the minimum wait after a connection is lost or refused before trying the next one. The reason is mundane. A reconnect loop with no delay hammers the tool's listener dozens of times a second, and then you can't see what the original problem was in the log.

I've seen hosts with T5 turned down to nearly nothing — shortened during development so it would reconnect fast, then shipped that way. Match the value to the equipment side rather than picking one; I have not verified against a tool what default the standard requires here.

Worth checking on the host

  • Does the shutdown path actually send Separate.req (SType 9), or does it just close the socket?
  • Does it wait for a reply after sending Separate.req? It shouldn't.
  • Does it close the socket after sending it? If it doesn't, the far end sits in NOT SELECTED and that connection becomes T7's problem — the NOT SELECTED timeout.
  • Does an abnormal process exit take the same path? Usually not, which makes the equipment's Linktest interval your last line of defence.

A blocked reconnect caused by one unsent Separate.req is the kind of bug where cause and symptom are far enough apart that it eats a day. Read the shutdown code first and it's a five-minute answer.

The captures above were made against the passive listener in the SECS/GEM simulator. Open a socket, send an SType 9, and you'll get the same bytes.