수강 신청
대학교 한 학기를 결정짓는 수강신청은 항상 긴장되기 마련입니다. 그러니 여러분은 어떻게 하면 수강신청에 실패하지 않을까 생각할 것입니다. 보통은 PC방을 가서 수강신청을 하거나 네이비즘과 같은 서버 시간 사이트를 씁니다.
수강신청을 위해 PC방을 가는 건 조금 이상합니다. PC방의 컴퓨터 환경이 집의 컴퓨터 환경보다 쾌적할 것은 분명하지만 좋은 컴퓨터 환경이 수강신청의 성공에 주는 영향은 아주 미미합니다. 그마저도 PC방 컴퓨터에서 집 컴퓨터에 비해 얻을 수 있는 몇 ms의 이득은 집에서 손가락 몇 ms 더 빠르게 놀리는 것으로 대체될 수 있고요 (물론 집에 컴퓨터가 없거나 아주 아주 아주 저열한 사양의 컴퓨터만 있다면 어쩔 수 없지만요). 패킷 전송 속도의 측면에서도 집의 인터넷 환경이 특별히 안 좋은 게 아니라면 큰 차이도 나지 않습니다. $^1$
네이비즘의 경우에는... KRISS에서 제공하는 UTCk3 시계와 비교했을 때 네이비즘 창을 여러 번 새로고침하자 UTCk3 시계보다 빠른 시간을 표시했다가, 더 느린 시간을 표시했다가 하며 바뀌는 모습을 보였습니다. 네이비즘에서 제공하는 정보가 정확한지도 알 수 없고, 네이비즘 서버에서는 제대로 된 정보를 제공했다고 하더라도 브라우저가 제대로 표시하지 못할 수도 있습니다. 둘 다 네이비즘 사이트에 표시되는 정보를 신뢰하기 어렵다는 의미가 되겠습니다.
돌아와서, 수강신청을 할 때 중요한 요소가 무엇이냐 하면 두 가지로 추측하는데 '버튼을 누르는 시점 (이하 $T_p$)' 과 '브라우저의 입력 정보가 서버에 도달할 때까지 걸리는 시간 (이하 $T_{c\to s}$)' 입니다. 이 때 목표는 $T_p + T_{c\to s} - (10:00)$을 최대한 작은 양의 값으로 만드는 것입니다 (음의 값이 되면 망합니다).
문제는 여기서 $T_p$를 판정하는 주체가 학교 서버라는 겁니다. 제가 다니고 있는 대학교는 따로 NTP 서버를 열어주지 않았기 때문에 서버에서 기준으로 사용하는 시간을 받아오기가 참 곤란합니다. 이럴 때는 어떻게 해야 할까요?
HTTP 헤더의 "Date"
일단 수강신청은 해야 하니까 수강신청 서버는 HTTP 통신을 합니다. HTTP의 응답 헤더(Response Header)에는 서버가 요청에 응답한 시점의 시간이 "Date"에 나타납니다. 문제는 그 정밀도인데, 초 단위까지만 표시되어 있습니다. 몇 밀리세컨드에서 응답했는지 이 데이터만으로는 알 수 없다는 겁니다. 일분 일초 일밀리초가 급한 수강신청 상황에서 최대 1초의 오차는 치명적입니다. 밀리초를 알아낼 방법은 없는 걸까요? 물론 있습니다.
현재 내 컴퓨터(클라이언트)의 시간을 $T_c$, 서버의 시간을 $T_s$라고 합시다. 또 편의를 위해 $T_{c \to s}$는 항상 1보다 작다고 합시다. (물론 항상 작을 겁니다.) $T_c, T_s$만을 고려하면 ($T_{c \to s}$은 상수로 둡니다) "Date"가 나타내는 시간은 ${\lfloor T_s + T_{c \to s}\rfloor}$입니다. 즉 ${\lfloor T_s + T_{c \to s}\rfloor} \leq T_s < {\lfloor T_s + T_{c \to s}\rfloor} + 1$임을 알 수 있고 여기서부터 탐색을 시작할 수 있습니다.
$T_s - T_c \in [a, b)$임을 안 상황에서 새로운 정보 $T_s - T_c \in [x, x + 1)$이 들어왔다면 $T_s - T_c \in [a, b) \cap [x, x + 1)$임을 알 수 있지요. $T_s - T_c$의 값 범위는 $[{\lfloor T_s + T_{c \to s}\rfloor} - T_c, {\lfloor T_s + T_{c \to s}\rfloor} + 1 - T_c)$로 얻어지므로 다양한 시간대에서 범위를 얻어 교집합을 계속 구하다 보면 특정 값으로 좁혀질 겁니다. xx:020일 때, xy:040일 때, xz:060일 때... 처럼 밀리세컨드 값이 조금씩 다른 시간대에서 해보면 괜찮겠네요. 물론 interval은 너무 짧게 두지 않도록 합시다.
$T_{c \to s}$
이제 서버와 싱크를 어느 정도 맞췄다면 $T_{c \to s}$를 구해 봅시다. 몇 가지 방법이 있습니다.
ping 혹은 tracert
ping을 쳐서 서버까지 패킷이 가는 데 걸리는 시간을 찾거나, ping이 막힌 경우 tracert를 통해 집에서 학교 서버 직전까지 걸리는 시간을 찾을 수 있습니다. 그 후 3way handshake에 얼마나 시간이 걸릴 지 예측해서 $T_{c \to s}$를 구하면 됩니다. 말은 쉬운데 저걸 어떻게 예측할지는 잘 모르겠습니다. 대충 3배 하면 되나요? 네트워크는 잘 몰라서요.
절묘한 $T_s$ 컨트롤
$T_{c \to s}$은 상수로 치고, $T_s + T_{c \to s}$을 정수로 만드는 $T_s$를 어떻게든 찾으면 "Date"를 받아와 $T_{c \to s}$를 찾을 수 있습니다. $T_s$의 실수부를 0.995에서 살짝살짝 낮추면서 ${\lfloor T_s + T_{c \to s}\rfloor}$ 값이 기대한 값보다 1 작아지는 지점을 찾고 이걸로 $T_{c \to s}$를 구한 다음 여러 번 반복해서 평균 내면 값이 나오지 싶습니다.
근데 해봤더니 안되네요
직접 해보기
앞서 설명한 대로 정보를 모으도록 코딩을 하고 결과를 봤습니다.
$T_s - T_c = 44.344306ms$
$T_{c \to s} = 6.5ms$
그렇다는데... 사실 $T_{c \to s}$는 너무 작은 값이라 오차가 좀 심하게 나더라고요. 그래서 그냥 없는 셈 치기로 했습니다.
이제 분석해봅시다. 서버 시간이 로컬 시간보다 44ms 빠릅니다. 이 말은 내 컴퓨터에서 0ms를 띄우면 서버는 44ms를 띄운다는 뜻입니다. 저는 10시 40ms쯤 전에 버튼을 누르면 거의 완벽한 수강신청이 가능하다는 뜻입니다....
근데 계산하면서 상수라고 치고 넘어가버린것도 많고 애초에 오차도 1~20ms 정도로 크게 잡았으니까 40ms면 의미 있는 값도 아닌 것 같아서... 그냥 컴퓨터 시간 맞춰서 누르기로 했습니다.
이번 학기 수강신청은 끝났고 내년 초에나 또 건드려 볼 주제