Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 4개 있습니다.)
.NET Framework: 333. 코드로 재현하는 소켓 상태(FIN_WAIT1, FIN_WAIT2, TIME_WAIT, CLOSE_WAIT, LAST_WAIT)
; https://www.sysnet.pe.kr/2/0/1334

.NET Framework: 334. 스레드 비정상 종료로 발생하는 CLOSE_WAIT 소켓 상태
; https://www.sysnet.pe.kr/2/0/1336

.NET Framework: 849. C# - Socket의 TIME_WAIT 상태를 없애는 방법
; https://www.sysnet.pe.kr/2/0/11996

.NET Framework: 2062. C# - 코드로 재현하는 소켓 상태(SYN_SENT, SYN_RECV)
; https://www.sysnet.pe.kr/2/0/13153




스레드 비정상 종료로 발생하는 CLOSE_WAIT 소켓 상태

보통, 서버 측 소켓에서는 Accept 후 클라이언트 측 과의 통신을 별도의 스레드에 할당해서 처리합니다. 간략하게 코드를 만들어 보면 다음과 같은 식인데요.

static void Main(string[] args)
{
    TcpListener listener = new TcpListener(6000);
    listener.Start();
            
    while (true)
    {
        TcpClient childSocket = listener.AcceptTcpClient();

        Thread thread = new Thread(tcpClient_Connected);
        thread.Start(childSocket); // 혹은 스레드 풀을 사용하거나.
    }
}

static void tcpClient_Connected(object obj)
{
    TcpClient socket = obj as TcpClient;
    Thread.Sleep(1000 * 10); // 테스트를 위해 10초 동안 대기
    socket.Close();
}

그런데, 여기서 만약 tcpClient_Connected 메서드를 처리하는 스레드가 비정상 종료하는 상황이 발생했다고 가정해 보겠습니다.

static void Main(string[] args)
{
    ... [생략] ...
    
        Thread thread = new Thread(tcpClient_Connected);
        thread.Start(childSocket);
        ThreadPool.QueueUserWorkItem(abortThread, thread);
    
    ... [생략] ...

}

static void tcpClient_Connected(object obj)
{
    ... [생략] ...
}

static void abortThread(object obj)
{
    Thread thread = obj as Thread;
    Thread.Sleep(5000); // 5초 후에 스레드 강제 종료
    thread.Abort();
}

이렇게 되면, 당연히 서버 측 대응 소켓은 Close 메서드를 호출하지 못하게 되고 클라이언트 측 소켓이 Close를 하는 시점에 CLOSE_WAIT으로 빠지게 됩니다.

CLOSE_WAIT 상태에 대해서는 지난번 글에서 자세히 설명했으므로 여기서는 넘어가겠습니다.

코드로 재현하는 소켓 상태(FIN_WAIT1, FIN_WAIT2, TIME_WAIT, CLOSE_WAIT, LAST_WAIT)
; https://www.sysnet.pe.kr/2/0/1334

이런 상황을 개선하려면 tcpClient_Connected 메서드 내에서 예외 처리를 추가해 줄 수 있습니다.

static void tcpClient_Connected(object obj)
{
    TcpClient socket = obj as TcpClient;

    try
    {
        Thread.Sleep(1000 * 10); // 테스트를 위해 10초 동안 대기
    } 
    catch { }
    finally
    {
        socket.Close();
    }
}

finally에 의해서 소켓은 닫히게 되도록 했지만, 그래도 뭔가 미심쩍습니다. 왜냐하면 스레드 비정상 종료 현상은 이후의 어떤 코드도 실행하지 못할 가능성이 더 크기 때문에 저 정도의 처리로는 부족하기 때문입니다.

바로 이럴 때 적합하게 사용할 수 있는 것이 CER(constrained execution region)입니다.

RuntimeHelpers.PrepareConstrainedRegions Method 
; https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions

예외처리 블록을 CER로 지정해 주면 해당 코드는 반드시 실행되도록 JIT 컴파일까지 미리 시켜 놓기 때문에 안전하게 Close 메서드를 호출할 수 있게 됩니다. 방법도 그다지 어렵지 않은데, 단순히 try 블록이 시작되기 전에 RuntimeHelpers.PrepareConstrainedRegions 메서드만 불러주면 됩니다.

그리하여, 스레드 비정상 종료에도 안전하게 소켓을 해제하는 코드는 다음의 형태를 갖게 됩니다.

static void tcpClient_Connected(object obj)
{
    TcpClient socket = obj as TcpClient;
    RuntimeHelpers.PrepareConstrainedRegions();
    try
    {
        Thread.Sleep(5000 * 60);
    }
    catch
    { }
    finally
    {
        socket.Close();
    }
}

단순한 TCP 서버에서는 위와 같은 처리를 안해도 무리는 없지만, 24시간/365일 구동되는 시스템이라면 꼭 필요한 장치일 수 있겠지요. ^^

(첨부파일은 위의 상황을 테스트 할 수 있는 간략한 예제입니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 6/27/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2012-08-31 08시57분
[Lyn] 으아... 닷넷 깊이 들어가면 끝도 없군요
[guest]
2012-08-31 02시37분
꼭 닷넷이라고 한정지을 필요는 없을 것 같습니다. 다른 플랫폼에서도, 스레드가 강제 종료하는 상황에서 자원을 해제할 수 있는 코드가 실행됨을 보장해야 하는 방법이 "내재해 있거나" 또는 "없거나" 또는 "제공되어야만" 할 테니까요. ^^
정성태

... 61  62  63  64  65  66  67  [68]  69  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
12299정성태8/27/202021331.NET Framework: 934. C# - ETW 관련 Win32 API 사용 예제 코드 (3) ETW Consumer 구현파일 다운로드1
12298정성태8/27/202021357오류 유형: 640. livekd - Could not resolve symbols for ntoskrnl.exe: MmPfnDatabase
12297정성태8/25/202020846개발 환경 구성: 503. SHA256 테스트 인증서 생성 방법
12296정성태8/24/202022145.NET Framework: 933. C# - ETW 관련 Win32 API 사용 예제 코드 (2) NT Kernel Logger파일 다운로드1
12295정성태8/24/202020966오류 유형: 639. Bitvise - Address is already in use; bind() in ListeningSocket::StartListening() failed: Windows error 10013: An attempt was made to access a socket ,,,
12293정성태8/24/202021747Windows: 171. "Administered port exclusions" 설명
12292정성태8/20/202025206.NET Framework: 932. C# - ETW 관련 Win32 API 사용 예제 코드 (1)파일 다운로드2
12291정성태8/15/202021414오류 유형: 638. error 1297: Device driver does not install on any devices, use primitive driver if this is intended.
12290정성태8/11/202023183.NET Framework: 931. C# - IP 주소에 따른 국가별 위치 확인 [8]파일 다운로드1
12289정성태8/6/202019981개발 환경 구성: 502. Portainer에 윈도우 컨테이너를 등록하는 방법
12288정성태8/5/202017354오류 유형: 637. WCF - The protocol 'net.tcp' does not have an implementation of HostedTransportConfiguration type registered.
12287정성태8/5/202020266오류 유형: 636. C# - libdl.so를 DllImport로 연결 시 docker container 내에서 System.DllNotFoundException 예외 발생
12286정성태8/5/202022109개발 환경 구성: 501. .NET Core 용 container 이미지 만들 때 unzip이 필요한 경우
12285정성태8/4/202020164오류 유형: 635. 윈도우 10 업데이트 - 0xc1900209 [2]
12284정성태8/4/202019546디버깅 기술: 169. Hyper-V의 VM에 대한 메모리 덤프를 뜨는 방법
12283정성태8/3/202021777디버깅 기술: 168. windbg - 필터 드라이버 확인하는 확장 명령어(!fltkd) [2]
12282정성태8/2/202018474디버깅 기술: 167. windbg 디버깅 사례: AppDomain 간의 static 변수 사용으로 인한 crash (2)
12281정성태8/2/202022971개발 환경 구성: 500. (PDB 연결이 없는) DLL의 소스 코드 디버깅을 dotPeek 도구로 해결하는 방법
12280정성태8/2/202019910오류 유형: 634. 오라클 (평생) 무료 클라우드 VM 생성 후 SSH 접속 시 키 오류 발생 [2]
12279정성태7/29/202022974개발 환경 구성: 499. 닷넷에서 접근해보는 InterSystems의 Cache 데이터베이스파일 다운로드1
12278정성태7/23/202018617VS.NET IDE: 149. ("Binary was not built with debug information" 상태로) 소스 코드 디버깅이 안되는 경우
12277정성태7/23/202020911개발 환경 구성: 498. DEVPATH 환경 변수의 사용 예 - .NET Reflector의 (PDB 연결이 없는) DLL의 소스 코드 디버깅
12276정성태7/23/202020910.NET Framework: 930. 개발자를 위한 닷넷 어셈블리 바인딩 - DEVPATH 환경 변수
12275정성태7/22/202022513개발 환경 구성: 497. 닷넷에서 접근해보는 InterSystems의 IRIS Data Platform 데이터베이스파일 다운로드1
12274정성태7/21/202021259개발 환경 구성: 496. Azure - Blob Storage Account의 Location 이전 방법 [1]파일 다운로드1
12273정성태7/18/202024744개발 환경 구성: 495. Azure - Location이 다른 웹/DB 서버의 경우 발생하는 성능 하락
... 61  62  63  64  65  66  67  [68]  69  70  71  72  73  74  75  ...