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

서버용 Socket에서 사용하는 포트가 충돌한다면?

일반적으로 소켓 서버의 경우 지정된 포트를 가지고 Listen을 하게 됩니다.

using System;
using System.Net;
using System.Net.Sockets;

class Program
{
    static void Main(string[] args)
    {
        int port = 7999;
        Socket listenSocket = new Socket(AddressFamily.InterNetwork,
                                     SocketType.Stream,
                                     ProtocolType.Tcp);

        IPEndPoint ep = new IPEndPoint(IPAddress.Any, port);
        listenSocket.Bind(ep);
        listenSocket.Listen(5);

        IPEndPoint bindingEndpoint = (listenSocket.LocalEndPoint as IPEndPoint);
        Console.WriteLine("[LISTEN] " + bindingEndpoint.Address + ":" + bindingEndpoint.Port); // [LISTEN] 0.0.0.0:7999
        Console.ReadLine();
    }
}

그런데, 하필 그 포트를 로컬에서 생성된 TCP 소켓이 사용하고 있다면 어떻게 될까요?

예를 들어, 다음과 같이 현재 시스템에 사용 중인 소켓을 나열해보면,

E:\>netstat -ano | findstr "EST"
  TCP    121.163.96.206:43611   74.125.203.125:5222    ESTABLISHED     6224
  ...[생략]...
  TCP    127.0.0.1:65001        127.0.0.1:41177        ESTABLISHED     3380

보시는 바와 같이 43611 포트가 사용중인데, 이때 이 포트로 LISTEN을 해보면,

int port = 43611;
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

IPEndPoint ep = new IPEndPoint(IPAddress.Any, port);
listenSocket.Bind(ep); // 예외 발생

Bind 메서드 호출 단계에서 다음과 같은 예외가 발생합니다.

E:\...\bin\Debug>ConsoleApplication1.exe

Unhandled Exception: System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddresssocketAddress)
   at System.Net.Sockets.Socket.Bind(EndPoint localEP)
   at Program.Main(String[] args) in e:\...\ConsoleApplication1\Program.cs:line 23

이건 ... 그야말로 확률 싸움입니다. 소켓 서버를 구현한다면 해당 포트가 클라이언트용 소켓 접속에 사용되고 있지 않음을 운좋게 바랄 수밖에 없습니다.




물론 ^^ 이런 문제를 해결할 수 있는 대안이 있습니다.

우선, 소켓 서버의 포트가 고정일 필요가 없다면 Bind 시에 포트 번호를 0으로 지정해 줄 수 있습니다.

Socket listenSocket = new Socket(AddressFamily.InterNetwork,
                                SocketType.Stream,
                                ProtocolType.Tcp);

IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
listenSocket.Bind(ep);
listenSocket.Listen(5);

IPEndPoint bindingEndpoint = (listenSocket.LocalEndPoint as IPEndPoint);
Console.WriteLine("[LISTEN] " + bindingEndpoint.Address + ":" + bindingEndpoint.Port); // 출력: [LISTEN] 0.0.0.0:46083

그럼, 시스템 측에서는 현재 비어 있는 소켓 포트를 하나 선정해서 LISTEN 포트로 사용합니다. 따라서 여유분의 포트만 있다면 적어도 포트 충돌이 발생할 위험은 없는 것입니다.

그런데, 이 방법이 그다지 현실적이진 않습니다. 왜냐하면 클라이언트 측에서 서버에 접속하려면 반드시 포트 번호를 알아야 하고, 그것이 가변적이라면 어떤 포트를 사용하게 될지 사전에 클라이언트 측에 알려줄 수 있는 또 다른 방법을 고안해야 하는 불편함이 있기 때문입니다.




다음 방법은? 클라이언트 소켓 연결에 사용될 포트와의 충돌을 막기 위해 윈도우 운영체제의 경우 "동적 포트"에 대한 범위를 지정할 수 있는 방법을 제공하고 있습니다.

The default dynamic port range for TCP/IP has changed in Windows Vista and in Windows Server 2008
; http://support.microsoft.com/kb/929851

서버 버전 별로 (TCP및 UDP에 대해) 다음과 같이 기본 예약되어 있습니다.

Windows Server 2003: 1024 ~ 5000
Windows Server 2008 이후: 49152 ~ 65535

동적 포트를 변경하기 위해서는 2003의 경우 Windows Server 2003 리소스 킷에 포함된,

Windows Server 2003 Resource Kit Tools 
; http://www.microsoft.com/en-us/download/details.aspx?id=17657

rpccfg.exe 프로그램을 사용해서,

Minimizing Windows Server 2003 network services 
; http://www.hsc.fr/ressources/breves/min_w2k3_net_srv.html.en

다음과 같이 원하는 범위를 지정할 수 있고,

C:\>rpccfg /pe 5050-5070
The following ports/port range will be used for Internet ports
        5050-5070

조회할 수 있습니다.

C:\>rpccfg /d 0
The following ports/port range will be used for Internet ports
        5050-5070

Windows Server 2008 이후로는 자체 내장된 netsh을 이용해 다음과 같이 조회할 수 있고,

C:\WINDOWS\system32>netsh int ipv4 show dynamicport tcp

Protocol tcp Dynamic Port Range
---------------------------------
Start Port      : 1025
Number of Ports : 64510

이렇게 쉽게 설정할 수 있습니다.

netsh int ipv4 set dynamicport tcp start=10000 num=1000
netsh int ipv4 set dynamicport udp start=10000 num=1000
netsh int ipv6 set dynamicport tcp start=10000 num=1000
netsh int ipv6 set dynamicport udp start=10000 num=1000

설정된 값은 레지스트리(2003의 경우, HKEY_LOCAL_MACHINE\Software\Microsoft\Rpc)에 기록됩니다.

How to configure RPC dynamic port allocation to work with firewalls 
; http://support.microsoft.com/kb/154596/en-us

기본적으로는 레지스트리에 아무런 값이 없습니다.

port_reserve_1.png

하지만, "rpccfg /pe 5050-5070"와 같이 한번이라도 실행해 주면 이렇게 포트 범위가 설정됩니다.

port_reserve_2.png

참고로, 기본 예약 포트의 범위는 운영체제의 서버/클라이언트 버전에 따라서도 다릅니다. 가령, 클라이언트 운영체제인 Windows 8.1의 경우 다음과 같은 예약 범위를 보여줍니다.

E:\>netsh int ipv4 show dynamicport tcp

Protocol tcp Dynamic Port Range
---------------------------------
Start Port      : 1025
Number of Ports : 64510




다행히 조금 더 공격적인 포트 제외 방법이 있습니다.

How to reserve a range of ephemeral ports on a computer that is running Windows Server 2003 or Windows 2000 Server 
; http://support.microsoft.com/kb/812873

You cannot exclude ports by using the ReservedPorts registry key in Windows Server 2008 or in Windows Server 2008 R2 
; http://support.microsoft.com/kb/2665809

가령 윈도우 8.1에서 다음과 같이 실행해 보면,

D:\>netsh int ipv4 show excludedportrange protocol=tcp

Protocol tcp Port Exclusion Ranges

Start Port    End Port
----------    --------
        80          80
      8012        8013
      8023        8025
      8090        8093

* - Administered port exclusions.

예약된 포트를 알 수 있습니다. 재미있는 것은, IIS 서버의 웹 사이트에 할당한 포트가 기본적으로 포함되어 있다는 것입니다. 따라서, IIS 웹 사이트용 포트는 절대 다른 프로그램에서 사용하는 TCP 소켓과 충돌이 발생하지 않습니다.

물론, 우리가 원하는 포트를 다음과 같은 명령어로 추가/삭제할 수 있습니다.

C:\Windows\system32>netsh int ipv4 Add excludedportrange protocol=tcp startport=1000 numberofports=5 store=persistent
Ok.


C:\Windows\system32>netsh int ipv4 delete excludedportrange protocol=tcp startport=1000 numberofports=5 store=persistent
Ok.

따라서, 서버에서 충돌이 발생할 수 있는 범위의 포트가 있다면 이런 식으로 예약해 두는 것이 좋습니다.




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

[연관 글]






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

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

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  52  53  54  [55]  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12269정성태7/16/20209237오류 유형: 632. .NET Core 웹 응용 프로그램 - The process was terminated due to an unhandled exception.
12268정성태7/15/202010982오류 유형: 631. .NET Core 웹 응용 프로그램 오류 - HTTP Error 500.35 - ANCM Multiple In-Process Applications in same Process
12267정성태7/15/202012591.NET Framework: 927. C# - 윈도우 프로그램에서 Credential Manager를 이용한 보안 정보 저장파일 다운로드1
12266정성태7/14/202010329오류 유형: 630. 사용자 계정을 지정해 CreateService API로 서비스를 등록한 경우 "Error 1069: The service did not start due to a logon failure." 오류발생
12265정성태7/10/20209433오류 유형: 629. Visual Studio - 웹 애플리케이션 실행 시 "Unable to connect to web server 'IIS Express'." 오류 발생
12264정성태7/9/202018554오류 유형: 628. docker: Error response from daemon: Conflict. The container name "..." is already in use by container "...".
12261정성태7/9/202011501VS.NET IDE: 148. 윈도우 10에서 .NET Core 응용 프로그램을 리눅스 환경에서 실행하는 2가지 방법 - docker, WSL 2 [5]
12260정성태7/8/20209759.NET Framework: 926. C# - ETW를 이용한 ThreadPool 스레드 감시파일 다운로드1
12259정성태7/8/20209352오류 유형: 627. nvlddmkm.sys의 BAD_POOL_HEADER BSOD 문제 [1]
12258정성태7/8/202012524기타: 77. DataDog APM 간략 소개
12257정성태7/7/20209462.NET Framework: 925. C# - ETW를 이용한 Monitor Enter/Exit 감시파일 다운로드1
12256정성태7/7/20209953.NET Framework: 924. C# - Reflection으로 변경할 수 없는 readonly 정적 필드 [4]
12255정성태7/6/202010396.NET Framework: 923. C# - ETW(Event Tracing for Windows)를 이용한 Finalizer 실행 감시파일 다운로드1
12254정성태7/2/202010219오류 유형: 626. git - REMOTE HOST IDENTIFICATION HAS CHANGED!
12253정성태7/2/202011366.NET Framework: 922. C# - .NET ThreadPool의 Local/Global Queue파일 다운로드1
12252정성태7/2/202013326.NET Framework: 921. C# - I/O 스레드를 사용한 비동기 소켓 서버/클라이언트파일 다운로드2
12251정성태7/1/202011367.NET Framework: 920. C# - 파일의 비동기 처리 유무에 따른 스레드 상황 [1]파일 다운로드2
12250정성태6/30/202013934.NET Framework: 919. C# - 닷넷에서의 진정한 비동기 호출을 가능케 하는 I/O 스레드 사용법 [1]파일 다운로드1
12249정성태6/29/202010089오류 유형: 625. Microsoft SQL Server 2019 RC1 Setup - 설치 제거 시 Warning 26003 오류 발생
12248정성태6/29/20208462오류 유형: 624. SQL 서버 오류 - service-specific error code 17051
12247정성태6/29/202010061.NET Framework: 918. C# - 불린 형 상수를 반환값으로 포함하는 3항 연산자 사용 시 단축 표현 권장(IDE0075) [2]파일 다운로드1
12246정성태6/29/202010863.NET Framework: 917. C# - USB 관련 ETW(Event Tracing for Windows)를 이용한 키보드 입력을 감지하는 방법
12245정성태6/24/202011362.NET Framework: 916. C# - Task.Yield 사용법 (2) [2]파일 다운로드1
12244정성태6/24/202011175.NET Framework: 915. ETW(Event Tracing for Windows)를 이용한 닷넷 프로그램의 내부 이벤트 활용 [1]파일 다운로드1
12243정성태6/23/20208731VS.NET IDE: 147. Visual C++ 프로젝트 - .NET Core EXE를 "Debugger Type"으로 지원하는 기능 추가
12242정성태6/23/20209519오류 유형: 623. AADSTS90072 - User account '...' from identity provider 'live.com' does not exist in tenant 'Microsoft Services'
... 46  47  48  49  50  51  52  53  54  [55]  56  57  58  59  60  ...