sgcWebSockets의 IOCP(윈도우) 및 EPOLL(리눅스) 서버 엔진은 연결마다 스레드 하나를 두는 대신 적은 수의 스레드 풀로 수천 개의 연결을 처리해요. 그래서 동시 접속 부하가 큰 서버에 적합한 선택이 돼요. 다만 지금까지는 인사 없이 조용히 사라지는 피어를 알아챌 방법이 없었어요. 새로 추가된 두 옵션인 TCPKeepAlive와 KeepAliveTimeout이 이 공백을 메워요.
조용히 사라진 피어는 이벤트를 발생시키지 않아요
IOCP와 EPOLL은 이벤트 기반으로 동작해요. 엔진은 운영체제가 소켓에 데이터가 있다거나, 연결이 닫혔다거나, 오류가 발생했다고 알려줄 때까지 잠들어 있어요. 이 방식은 피어가 정상적인 FIN이나 RST 없이 사라지기 전까지는 잘 작동해요. 예를 들어 클라이언트가 충돌하거나, NAT나 방화벽이 유휴 매핑을 삭제하거나, 경로가 조용히 바뀌는 경우가 그래요. 이런 경우 해당 소켓으로는 어떤 이벤트도 도착하지 않아서, 서버에서는 연결이 ESTABLISHED 상태로 무기한 남아 있게 돼요.
전통적인 연결당 스레드(thread-per-connection) IOHandler에는 이런 문제가 없어요. 각 연결이 타임아웃이 있는 읽기 작업에서 블록되기 때문에, 조용해진 연결은 다음 읽기 타임아웃 때 감지되어 해제되거든요. IOCP와 EPOLL에는 이에 상응하는 메커니즘이 없었는데, 바로 이 두 새 옵션이 그 역할을 채워줘요.
TCPKeepAlive: 운영체제가 감시하도록 맡기기
TCPKeepAlive를 활성화하면 운영체제가 직접 연결을 감시해요. 연결이 Time초 동안 유휴 상태가 되면 프로브를 시작하고, 피어가 응답하거나 연결이 끊길 때까지 Interval초마다 반복해요. 기본값은 꺼짐이며, 아래 값은 활성화했을 때의 기본값과 같아요.
Server.IOHandlerOptions.IOHandlerType := iohEPOLL;
Server.IOHandlerOptions.EPOLL.TCPKeepAlive.Enabled := True;
Server.IOHandlerOptions.EPOLL.TCPKeepAlive.Time := 60; // seconds idle before probing
Server.IOHandlerOptions.EPOLL.TCPKeepAlive.Interval := 10; // seconds between probes
윈도우 서버에서는 동일한 세 속성이 IOHandlerOptions.IOCP.TCPKeepAlive 아래에 있어요.
KeepAliveTimeout: 애플리케이션 수준의 유휴 타임아웃
KeepAliveTimeout은 좀 더 직접적인 방식을 사용해요. 운영체제의 킵얼라이브 프로브가 어떤 결론을 내리든 상관없이, 지정한 초 이상 유휴 상태로 남아 있는 연결을 모두 닫아요. 이는 연결당 스레드 IOHandler가 이미 조용한 연결을 해제하는 방식과 가장 비슷해요. 기본값은 0으로 비활성화되어 있고, 정말로 유휴 상태인 연결에만 영향을 미치므로 요청을 읽는 중이거나 응답을 쓰는 중인 연결은 절대 중단되지 않아요.
Server.IOHandlerOptions.EPOLL.KeepAliveTimeout := 120; // seconds
// Windows
Server.IOHandlerOptions.IOCP.KeepAliveTimeout := 120;
예상되는 가장 느린 요청이나 응답보다 여유 있게 큰 값으로 설정하세요. 예를 들어 일반적인 HTTP나 WebSocket 서버라면 120초 정도예요. 이렇게 하면 느릴 뿐인 연결은 건드리지 않으면서 정말로 죽은 연결만 빠르게 정리할 수 있어요.
연결 끊김을 감지하는 세 번째 방법
sgcWebSockets는 이미 피어가 사라졌음을 알아채는 두 가지 방법을 제공했어요. 하나는 TsgcWebSocketClient에서 CleanDisconnect를 활성화해 정상적으로 동작하는 클라이언트가 사라지기 전에 서버에 알리도록 하는 방법이고, 다른 하나는 일정 간격으로 모든 클라이언트에 핑을 보내는 서버 측 heartbeat를 실행하는 방법이에요. 두 방법 모두 협력이 필요해요. 첫 번째는 클라이언트의 협력이, 두 번째는 여러분이 직접 구현한 핑/퐁 왕복이 필요하죠. TCPKeepAlive와 KeepAliveTimeout은 이런 협력이 전혀 필요 없는 첫 번째 옵션이에요. 서버가 온전히 스스로 죽은 연결을 감지해요.
제공 현황
두 옵션 모두 Delphi 및 C++ Builder 7부터 13까지의 sgcWebSockets에서 지금 바로 사용할 수 있어요. 기존 EPOLLThreads, IOCPThreads, WorkOpThreads 설정과 나란히 IOHandlerOptions.EPOLL과 IOHandlerOptions.IOCP 아래에 위치해요. 기본값은 꺼짐이므로 직접 켜지 않는 한 기존 서버는 아무것도 달라지지 않아요.
체험판을 내려받고 sgcWebSockets 제품 페이지에서 IOCP와 EPOLL에 대한 전체 레퍼런스를 확인해 보세요.
질문이나 의견이 있으신가요? 문의하기를 통해 연락해 주세요. 실제로 코드를 작성한 사람들이 직접 답변해 드려요.
