← 전체 글
SECS/GEM/약 8분 읽기/ 조회

수락된 Select와 거절된 Select는 한 바이트 차이다

실제 소켓으로 받은 Select.rsp 캡처. 수락, SEMI E37 Select Status 1·2·3 거절, 다른 SessionID로 온 응답. select failed 한 줄이 버리는 정보가 어느 바이트에 있는지.

SECS/GEMMESSCADA문제 해결체크리스트

"Select failed." 호스트 스택이 남기는 건 대개 이 한 줄이다. 장비가 거절한 건지, 엉뚱한 응답이 온 건지, 아예 안 온 건지는 이 줄만 봐서는 알 수 없다. 그런데 그 정보는 이미 와 있었다. 응답 프레임 안에 있다.

Select.rsp는 14바이트다. 아래 세 개는 전부 같은 요청에 대한 응답이고, 서로 딱 한 필드씩만 다르다.

수락

TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 04 01
RX Select.rsp   00 00 00 0A 00 0B 00 00 00 02 00 00 04 01

SessionID 00 0B(11)로 보냈고 00 0B로 돌아왔다. SystemBytes 00 00 04 01도 그대로다. SType은 01(Select.req)에서 02(Select.rsp)로 바뀌었다. 그리고 헤더의 Byte 3 — E37은 헤더 바이트를 0부터 세므로 순서로는 네 번째다 — 가 00이다.

이 Byte 3이 Select Status다. 데이터 메시지에서 Function 번호가 들어가는 자리를 컨트롤 메시지에서는 이 용도로 쓴다. 0이면 수락이다.

거절

TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 04 04
RX Select.rsp   00 00 00 0A 00 0B 00 03 00 02 00 00 04 04

길이 같다. SessionID 같다. SystemBytes 그대로 반향됐다. SType도 02, 정상적인 Select.rsp다. 다른 건 한 군데, Byte 3이 00이 아니라 03이다.

장비는 정상적으로 대답했다. 대답의 내용이 "안 된다"였을 뿐이다. 타임아웃도 아니고 네트워크 문제도 아니다. 그런데 Select Status를 로그로 안 남기는 호스트 스택을 쓰고 있으면 이 상황과 무응답이 똑같은 한 줄로 보인다.

이 숫자가 뜻하는 것

낮은 Select Status 값에는 SEMI E37이 이름을 붙여 뒀다. 아래 표는 표준에서 인용한 것이지 측정한 값이 아니다. 뒤이어 나오는 캡처가 증명하는 건 장비가 넣은 값이 그 바이트에 그대로 실린다는 사실뿐이다.

Select StatusSEMI E37 정의
0Communication Established — 수락
1Communication Already Active
2Connection Not Ready
3Connect Exhaust
4 이상E37과 하위 표준이 예약한 구간. 실무에서는 벤더 영역이니 매뉴얼을 봐야 한다

호스트 개발자가 제일 많이 걸리는 건 1이다. 장비가 고장 났다는 뜻이 아니다. 이미 누가 세션을 잡고 있고, 내 Select가 두 번째라는 뜻이다.

같은 listener에서 코드별로 하나씩, 실제 소켓으로 받아낸 거절 세 개다.

TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 04 02
RX Select.rsp   00 00 00 0A 00 0B 00 01 00 02 00 00 04 02   Select Status 1

TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 04 03
RX Select.rsp   00 00 00 0A 00 0B 00 02 00 02 00 00 04 03   Select Status 2

TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 04 04
RX Select.rsp   00 00 00 0A 00 0B 00 03 00 02 00 00 04 04   Select Status 3

움직이는 건 Byte 3 하나뿐이다. Length, SessionID, Header Byte 2, PType, SType, 반향된 SystemBytes까지 셋이 바이트 단위로 동일하다. "select failed" 한 줄짜리 로그의 문제가 바로 이거다. 유일하게 달라진 필드를 버린다.

어떤 장비가 어떤 상황에서 어떤 코드를 보내는지는 여전히 벤더 매뉴얼을 봐야 한다. 위 시뮬레이터는 코드를 스스로 고르지 않는다. 설정해 준 status를 그대로 실어 보낼 뿐이다.

장비 쪽에는 남지만 호스트에서는 보이지 않는 것도 하나 있다. 거절된 세 번 모두 listener의 HSMS 상태는 NOT CONNECTED 그대로였고, 수락된 교환에서만 SELECTED로 넘어갔다. 그 네 경우 전부 TCP 소켓은 열려 있었다. 소켓이 열려 있다고 세션이 열린 게 아니다.

실무에서는 이 값 하나가 회의를 끝낸다. "Select가 안 됩니다"와 "Select Status 3으로 거절당했습니다"는 벤더 입장에서 완전히 다른 티켓이다.

다른 SessionID로 돌아온 응답

TX Select.req   00 00 00 0A 00 0B 00 00 00 01 00 00 04 05
RX Select.rsp   00 00 00 0A 00 0C 00 00 00 02 00 00 04 05

Select Status는 00, 수락이다. SystemBytes도 00 00 04 05으로 정확히 반향됐다. 그런데 SessionID가 00 0B(11)로 보냈는데 00 0C(12)로 돌아왔다.

여기서부터는 호스트 구현에 달렸다. SystemBytes만 보고 트랜잭션을 짝짓는 구현이라면 이 응답을 받아들이고 세션이 열렸다고 판단한다 — 그리고 이어지는 데이터 메시지에서 장비와 서로 다른 SessionID를 쓰게 된다. SessionID까지 검증하는 구현이라면 버리고 타임아웃을 낸다. 어느 쪽이든 증상은 나중에, Select가 아니라 데이터 단계에서 이상하게 나타난다.

자기가 쓰는 스택이 어느 쪽인지 모르면 시뮬레이터에 붙여서 확인하는 게 빠르다. 아래에 재현 방법이 있다.

정리하면 볼 곳은 세 군데다

Select.rsp를 잡았을 때 순서대로 확인한다.

  1. SType02인가. 아니면 Select.rsp가 아니다.
  2. Byte 3 (Select Status)00이면 수락, 아니면 거절이고 그 숫자가 이유다. 1, 2, 3에는 E37이 이름을 붙여 뒀다. 위 표를 보면 된다.
  3. SessionID와 SystemBytes — 내가 보낸 값과 같은가. 다르면 응답은 왔지만 내 트랜잭션의 것이 아니다.

세 군데 다 맞으면 세션은 SELECTED다. 하나라도 어긋나면 그 어긋난 필드가 곧 증상의 이름이다.

직접 재현해보기

세 캡처 모두 SECS/GEM 시뮬레이터에서 만들었다. 장비에 selectRefuse를 걸면 두 번째, wrongSessionId를 걸면 세 번째가 나온다. 자기 호스트 스택을 붙여놓고 각각에서 로그에 뭐가 남는지 보면, Select Status를 남기는 구현인지 아닌지가 바로 드러난다.

응답이 아예 안 오는 쪽이라면 응답 없는 Select의 세 가지 원인을, SystemBytes가 어긋나는 쪽이라면 그 케이스만 따로 다룬 글을 보면 된다.