Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

.NET Remoting에서 서비스 호출 시 SYN_SENT로 남는 현상

TCP 상태 다이어그램에 보면,

[출처: https://www.cisco.com/c/en/us/about/press/internet-protocol-journal/back-issues/table-contents-34/syn-flooding-attacks.html]
tcp_connect_1.jpg

SYN_SENT는 연결을 시도한 측에서 SYN 패킷을 보낸 후 대상이 ACK로 반응하기까지의 상태입니다. 다른 말로 하면, ACK 받기 전까지는 connect 한 측의 소켓이 SYN_SENT로 계속 머무른다는 것입니다. 이것을 재현하는 간단한 방법은, 서버 측 소켓에 backlog 값을 작게 주어 Accept 후 지연 시간을 주면,

using (_serverSocket = CreateServerSocket())
{
    _serverSocket.Listen(1);

    while (_serverSocket != null)
    {
        WriteLog("accept...");
        using (Socket clntSocket = _serverSocket.Accept())
        {
            Thread.Sleep(1000 * 60 * 5);
            clntSocket.Close();
        }
    }
}

클라이언트에서 접속 시,

static void Main(string[] args)
{
    while (true)
    {
        Thread t2 = new Thread(clntSockFunc);
        t2.Start();

        Console.WriteLine("Press any key to continue...");
        Console.ReadLine();
    }
}

private static void clntSockFunc(object obj)
{
    try
    {
        using (var socket = CreateClientSocket())
        {
            IPEndPoint ipEp = new IPEndPoint(IPAddress.Loopback, 57102);
            socket.Connect(ipEp);

            socket.Close();
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
}

처음 한 번의 접속은 Accept를 타게 되고, 이후 한 번은 Listen(1)로 인해 큐에 담기고 3번째 접속부터 다음과 같은 예외가 발생합니다.

System.Net.Sockets.SocketException (0x80004005): No connection could be made because the target machine actively refused it 127.0.0.1:57102
   at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
   at Program.clntSockFunc(Object obj) in c:\temp\ConsoleApp2\Program.cs:line 31

그리고 위와 같은 오류가 발생하기 전, ^^ 재빠르게 netstat 명령어로 확인하면 다음과 같이 SYN_SENT를 볼 수 있습니다.

C:\Windows\System32> netstat -ano | findstr 57102
  TCP    127.0.0.1:2981         127.0.0.1:57102        ESTABLISHED     32564
  TCP    127.0.0.1:2983         127.0.0.1:57102        ESTABLISHED     32564
  TCP    127.0.0.1:2984         127.0.0.1:57102        SYN_SENT        32564
  TCP    127.0.0.1:57102        0.0.0.0:0              LISTENING       38696
  TCP    127.0.0.1:57102        127.0.0.1:2981         ESTABLISHED     38696
  TCP    127.0.0.1:57102        127.0.0.1:2983         ESTABLISHED     38696

또는 서버 측 대응 소켓이 없는 상태에서 클라이언트가 아무 데나 연결을 시도하면 역시 동일한 0x80004005 예외가 발생하고 그때도 잠시 SYN_SENT를 볼 수 있습니다. TIME_WAIT 과는 달리 삭제까지 별도로 대기 시간이 필요 없기 때문에 아주 잠시 SYN_SENT가 발생하므로 현업에서는 잘 볼 수 없습니다.




재미있는 것은, 이런 현상이 Windows Server 2008 x86 운영체제에서 .NET Remoting을 열었을 때 접속하려는 클라이언트 측에서 발생했습니다. SYN_SENT가 쌓이는데 시간이 지나도 끊어지지 않는다는 특징이 있습니다. 그래서 Socket.Connect를 호출한 측의 스레드가 블로킹되어 서비스 장애까지 간 것입니다.

재현 코드는 간단하게 .NET Remoing 서버를 다음과 같이 만들고,

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

class Program
{
    static TcpChannel tcpChannel;

    static void Main(string[] args)
    {
        RunServer();

        Console.WriteLine("Prees any key to run client...");
        Console.ReadLine();
    }

    static void RunServer()
    {
        try
        {
            /*
            Server Channel Properties
            ; https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/bb397831(v=vs.100)
            */
            System.Collections.IDictionary tcpProperties = new System.Collections.Hashtable();
            tcpProperties["port"] = HWConfig.TcpChannelPort;

            BinaryServerFormatterSinkProvider tcpServerProv = new BinaryServerFormatterSinkProvider();
            tcpServerProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Low;

            tcpChannel = new TcpChannel(tcpProperties, null, tcpServerProv);
            System.Diagnostics.Trace.WriteLine("TcpChannelPort is acquired!");

            ChannelServices.RegisterChannel(tcpChannel, false);
            RemotingConfiguration.RegisterWellKnownServiceType(typeof(HWSingleton), "HWSingleton", WellKnownObjectMode.Singleton);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}

클라이언트 측은 이렇습니다.

using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

class Program
{
    static void Main(string[] args)
    {
        string tcpServer = string.Format("tcp://localhost:{0}/HWSingleton", HWConfig.TcpChannelPort);
        Console.WriteLine(tcpServer);
        RegisterClient();

        HWSingleton hwSingleton = (HWSingleton)Activator.GetObject(typeof(HWSingleton), tcpServer);
        Console.WriteLine(hwSingleton.Echo("Hello"));
    }

    private static void RegisterClient()
    {
        /*
        Client Channel Properties
        ; https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/bb397839(v=vs.100)
        */

        System.Collections.IDictionary tcpProperties = new System.Collections.Hashtable();
        BinaryServerFormatterSinkProvider tcpServerProv = new BinaryServerFormatterSinkProvider();
        tcpServerProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Low;
        TcpChannel tcpChannel = new TcpChannel(tcpProperties, null, tcpServerProv);

        ChannelServices.RegisterChannel(tcpChannel, false);
    }
}

이렇게 하고 실행했더니, 다른 컴퓨터에서는 괜찮은데 유독 Windows Server 2008 x86에서만 클라이언트 측의 접속이 SYN_SENT로 머무르면서 절대 소켓 연결 시도가 끊기질 않습니다.




결국 해결 방법은 못 찾았습니다. 재미있는 것은 해당 시스템의 Tcpip 관련 레지스트리(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters) 설정이 좀 이상했다는 점입니다. 가령 다음과 같은 값들이 그랬습니다.

TcpTimedWaitDelay ffffffff
TcpFinWait2Delay ffffffff
KeepAliveInterval ffffffff
KeepAliveTime ffffffff
EnablePMTUDiscovery ff
TcpMaxDataRetransmissions ff

아무래도 Tcpip 관련 옵션들에서 어떤 문제가 발생한 듯싶은데 저런 값들을 다른 시스템에 맞춰 값을 조절하거나 삭제를 하기도 했지만 저 문제가 없어지지는 않았습니다.

그런데, 재미있는 점이 하나 있었다면 접속 측에서 "localhost"가 아닌 IP 주소로 접속(예: 127.0.0.1)하면 잘 되었습니다. 그래서 HOSTS 파일의 localhost 등록을 명시적으로 임시 설정하고,

127.0.0.1 localhost

소스 코드의 접속 관련 코드들에서 "localhost"를 "127.0.0.1"로 바꾼 후 HOSTS 파일을 다시 원복하는 것으로 문제를 우회 해결했습니다. (다행히 테스트 서버에서 발생한 문제였습니다. ^^)

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




참고로, .NET Remoting은 이제 역사의 뒤안길로 사라지는 기술이 되었습니다. 마이크로소프트는 향후 .NET 5로의 통합에서 더 이상 .NET Remoting(뿐만 아니라 WCF까지도!)을 지원하지 않기로 했으며 그 대체재로 gRPC를 추가하기로 결정했습니다.




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/7/2023]

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

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...
NoWriterDateCnt.TitleFile(s)
11820정성태2/20/201915232오류 유형: 515. 윈도우 10 1809 업데이트 후 "User Profiles Service" 1534 경고 발생
11819정성태2/20/201913904Windows: 158. 컴퓨터와 사용자의 SID(security identifier) 확인 방법
11818정성태2/20/201912814VS.NET IDE: 131. Visual Studio 2019 Preview의 닷넷 프로젝트 빌드가 20초 이상 걸리는 경우 [2]
11817정성태2/17/20199972오류 유형: 514. WinDbg Preview 실행 오류 - Error : DbgX.dll : WindowsDebugger.WindowsDebuggerException: Could not load dbgeng.dll
11816정성태2/17/201912477Windows: 157. 윈도우 스토어 앱(Microsoft Store App)을 명령행에서 직접 실행하는 방법
11815정성태2/14/201911365오류 유형: 513. Visual Studio 2019 - VSIX 설치 시 "The extension cannot be installed to this product due to prerequisites that cannot be resolved." 오류 발생
11814정성태2/12/201910199오류 유형: 512. VM(가상 머신)의 NT 서비스들이 자동 시작되지 않는 문제
11813정성태2/12/201911802.NET Framework: 809. C# - ("Save File Dialog" 등의) 대화 창에 확장 속성을 보이는 방법
11812정성태2/11/20199627오류 유형: 511. Windows Server 2003 VM 부팅 후 로그인 시점에 0xC0000005 BSOD 발생
11811정성태2/11/201913415오류 유형: 510. 서버 운영체제에 NVIDIA GeForce Experience 실행 시 wlanapi.dll 누락 문제
11810정성태2/11/201911467.NET Framework: 808. .NET Profiler - GAC 모듈에서 GAC 비-등록 모듈을 참조하는 경우의 문제
11809정성태2/11/201912989.NET Framework: 807. ClrMD를 이용해 메모리 덤프 파일로부터 특정 인스턴스를 참조하고 있는 소유자 확인
11808정성태2/8/201914048디버깅 기술: 123. windbg - 닷넷 응용 프로그램의 메모리 누수 분석
11807정성태1/29/201912359Windows: 156. 가상 디스크의 용량을 복구 파티션으로 인해 늘리지 못하는 경우 [4]
11806정성태1/29/201912106디버깅 기술: 122. windbg - 덤프 파일로부터 PID와 환경 변수 등의 정보를 구하는 방법
11805정성태1/28/201913938.NET Framework: 806. C# - int []와 object []의 차이로 이해하는 제네릭의 필요성 [4]파일 다운로드1
11804정성태1/24/201911978Windows: 155. diskpart - remove letter 이후 재부팅 시 다시 드라이브 문자가 할당되는 경우
11803정성태1/10/201911481디버깅 기술: 121. windbg - 닷넷 Finalizer 스레드가 멈춰있는 현상
11802정성태1/7/201912856.NET Framework: 805. 두 개의 윈도우를 각각 실행하는 방법(Windows Forms, WPF)파일 다운로드1
11801정성태1/1/201913904개발 환경 구성: 427. Netsh의 네트워크 모니터링 기능 [3]
11800정성태12/28/201813179오류 유형: 509. WCF 호출 오류 메시지 - System.ServiceModel.CommunicationException: Internal Server Error
11799정성태12/19/201813991.NET Framework: 804. WPF(또는 WinForm)에서 UWP UI 구성 요소 사용하는 방법 [3]파일 다운로드1
11798정성태12/19/201813221개발 환경 구성: 426. vcpkg - "Building vcpkg.exe failed. Please ensure you have installed Visual Studio with the Desktop C++ workload and the Windows SDK for Desktop C++"
11797정성태12/19/201810581개발 환경 구성: 425. vcpkg - CMake Error: Problem with archive_write_header(): Can't create '' 빌드 오류
11796정성태12/19/201810228개발 환경 구성: 424. vcpkg - "File does not have expected hash" 오류를 무시하는 방법
11795정성태12/19/201812654Windows: 154. PowerShell - Zone 별로 DNS 레코드 유형 정보 조회 [1]
... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...