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

비밀번호

댓글 작성자
 




... 151  152  153  [154]  155  156  157  158  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1203정성태12/21/201125588제니퍼 .NET: 18. MEF가 적용된 ASP.NET 웹 사이트를 제니퍼 닷넷으로 모니터링 해본 결과! [6]
1202정성태12/21/201126087오류 유형: 144. The database '...' cannot be opened because it is version 661.
1201정성태12/14/201141134디버깅 기술: 47. .NET Reflector를 이용한 "소스 코드가 없는" 어셈블리 디버깅 [4]
1200정성태12/11/201126971디버깅 기술: 46. Windbg 확장 DLL 만들기 (2) - Debugger Extension API 사용파일 다운로드1
1199정성태12/11/201128374VC++: 55. JNI DLL 컴파일 시 x86과 x64의 Export된 함수의 이름이 왜 다를까요? [2]파일 다운로드1
1198정성태12/9/201132195디버깅 기술: 45. Windbg 확장 DLL 만들기 (1) - 스레드를 강제 종료시키는 명령어 [2]파일 다운로드1
1197정성태12/9/201129968.NET Framework: 282. Shader 강좌와 함께 배워보는 XNA Framework (2) - RenderMonkey의 Shader/Model 파일 연동파일 다운로드2
1196정성태12/9/201133160.NET Framework: 281. Shader 강좌와 함께 배워보는 XNA Framework (1) - 기초 프로그램 구조 [3]파일 다운로드2
1195정성태12/8/201147802오류 유형: 143. DXSDK_Jun10.exe 설치 시 "Error Code: S1023" 오류 해결하는 방법 [4]
1194정성태12/8/201135585개발 환경 구성: 137. Visual C++ 런타임 구성요소에 대한 디버그 버전 설치하는 방법
1193정성태12/8/201122643오류 유형: 142. Windows Phone SDK 7.1 설치 시 Expression Blend 제거를 요구하는 경우
1192정성태12/8/201125675개발 환경 구성: 136. Windows 7 SP1의 IIS에서 사용자 프로파일을 로드하는 방법
1191정성태12/6/201126826.NET Framework: 280. MVC3에서 JavaScriptSerializer 재정의하는 방법파일 다운로드1
1190정성태12/6/201129995오류 유형: 141. Visual C++ 컴파일 오류 - error C2275: 'xxxxx' : illegal use of this type as an expression [1]
1189정성태12/6/201137061VS.NET IDE: 70. Visual Studio에서 프로젝트 로드가 안된다면?
1188정성태12/3/201126162개발 환경 구성: 135. 마이크로소프트 TFS 호스팅 서비스 - Preview [3]
1187정성태12/2/201130820개발 환경 구성: 134. Robocopy 오류 및 종료 코드
1186정성태12/1/201132683.NET Framework: 279. WPF - 그리기 성능 및 Blurring 문제파일 다운로드1
1185정성태11/29/201123404.NET Framework: 278. WPF - Content의 Changed 이벤트에 해당하는게 뭔가요?파일 다운로드1
1184정성태11/29/201126208.NET Framework: 277. F#과 WPF가 어울리지 못하는 근본적인 이유 [2]
1183정성태11/26/201121715오류 유형: 140. Visual Studio 2010 - Floating된 에디트 윈도우가 사라지지 않는 경우 [2]
1182정성태11/25/201157437.NET Framework: 276. 중복 없는 숫자를 랜덤으로 배열하는 방법 [5]파일 다운로드1
1181정성태11/24/201127922디버깅 기술: 44. windbg의 mscordacwks DLL 로드 문제
1180정성태11/23/201137707.NET Framework: 275. 레지스트리 등록 및 Interop DLL 없이 COM 개체 사용하는 방법 [2]파일 다운로드1
1179정성태11/22/201128289.NET Framework: 274. ReaderWriterLockSlim은 언제 쓰는 걸까요? [4]파일 다운로드1
1178정성태11/19/201124807.NET Framework: 273. 설치된 .NET 버전에 민감한 코드를 포함하는 경우, 다중으로 어셈블리를 만들어야 할까요?파일 다운로드1
... 151  152  153  [154]  155  156  157  158  159  160  161  162  163  164  165  ...