성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>서버용 Socket에서 사용하는 포트가 충돌한다면?</h1> <p> 일반적으로 소켓 서버의 경우 지정된 포트를 가지고 Listen을 하게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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(); } } </pre> <br /> 그런데, 하필 그 포트를 로컬에서 생성된 TCP 소켓이 사용하고 있다면 어떻게 될까요?<br /> <br /> 예를 들어, 다음과 같이 현재 시스템에 사용 중인 소켓을 나열해보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > E:\><span style='color: blue; font-weight: bold'>netstat -ano | findstr "EST"</span> TCP 121.163.96.206:<span style='color: blue; font-weight: bold'>43611</span> 74.125.203.125:5222 ESTABLISHED 6224 ...[생략]... TCP 127.0.0.1:65001 127.0.0.1:41177 ESTABLISHED 3380 </pre> <br /> 보시는 바와 같이 43611 포트가 사용중인데, 이때 이 포트로 LISTEN을 해보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int port = <span style='color: blue; font-weight: bold'>43611</span>; Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ep = new IPEndPoint(IPAddress.Any, port); listenSocket.Bind(ep); // 예외 발생 </pre> <br /> Bind 메서드 호출 단계에서 다음과 같은 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > E:\...\bin\Debug>ConsoleApplication1.exe <span style='color: blue; font-weight: bold'>Unhandled Exception: System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted</span> 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 </pre> <br /> 이건 ... 그야말로 확률 싸움입니다. 소켓 서버를 구현한다면 해당 포트가 클라이언트용 소켓 접속에 사용되고 있지 않음을 운좋게 바랄 수밖에 없습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 물론 ^^ 이런 문제를 해결할 수 있는 대안이 있습니다.<br /> <br /> 우선, 소켓 서버의 포트가 고정일 필요가 없다면 Bind 시에 포트 번호를 0으로 지정해 줄 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ep = new <span style='color: blue; font-weight: bold'>IPEndPoint(IPAddress.Any, 0);</span> 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 </pre> <br /> 그럼, 시스템 측에서는 현재 비어 있는 소켓 포트를 하나 선정해서 LISTEN 포트로 사용합니다. 따라서 여유분의 포트만 있다면 적어도 포트 충돌이 발생할 위험은 없는 것입니다.<br /> <br /> 그런데, 이 방법이 그다지 현실적이진 않습니다. 왜냐하면 클라이언트 측에서 서버에 접속하려면 반드시 포트 번호를 알아야 하고, 그것이 가변적이라면 어떤 포트를 사용하게 될지 사전에 클라이언트 측에 알려줄 수 있는 또 다른 방법을 고안해야 하는 불편함이 있기 때문입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 다음 방법은? 클라이언트 소켓 연결에 사용될 포트와의 충돌을 막기 위해 윈도우 운영체제의 경우 "동적 포트"에 대한 범위를 지정할 수 있는 방법을 제공하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > The default dynamic port range for TCP/IP has changed in Windows Vista and in Windows Server 2008 ; <a target='tab' href='http://support.microsoft.com/kb/929851'>http://support.microsoft.com/kb/929851</a> </pre> <br /> 서버 버전 별로 (TCP및 UDP에 대해) 다음과 같이 기본 예약되어 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows Server 2003: 1024 ~ 5000 Windows Server 2008 이후: 49152 ~ 65535 </pre> <br /> 동적 포트를 변경하기 위해서는 2003의 경우 Windows Server 2003 리소스 킷에 포함된,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows Server 2003 Resource Kit Tools ; <a target='tab' href='http://www.microsoft.com/en-us/download/details.aspx?id=17657'>http://www.microsoft.com/en-us/download/details.aspx?id=17657</a> </pre> <br /> rpccfg.exe 프로그램을 사용해서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Minimizing Windows Server 2003 network services ; <a target='tab' href='http://www.hsc.fr/ressources/breves/min_w2k3_net_srv.html.en'>http://www.hsc.fr/ressources/breves/min_w2k3_net_srv.html.en</a> </pre> <br /> 다음과 같이 원하는 범위를 지정할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\><span style='color: blue; font-weight: bold'>rpccfg /pe 5050-5070</span> The following ports/port range will be used for Internet ports 5050-5070 </pre> <br /> 조회할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\><span style='color: blue; font-weight: bold'>rpccfg /d 0</span> The following ports/port range will be used for Internet ports 5050-5070 </pre> <br /> Windows Server 2008 이후로는 자체 내장된 netsh을 이용해 다음과 같이 조회할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\WINDOWS\system32><span style='color: blue; font-weight: bold'>netsh int ipv4 show dynamicport tcp</span> Protocol tcp Dynamic Port Range --------------------------------- Start Port : 1025 Number of Ports : 64510 </pre> <br /> 이렇게 쉽게 설정할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 </pre> <br /> 설정된 값은 레지스트리(2003의 경우, HKEY_LOCAL_MACHINE\Software\Microsoft\Rpc)에 기록됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > How to configure RPC dynamic port allocation to work with firewalls ; <a target='tab' href='http://support.microsoft.com/kb/154596/en-us'>http://support.microsoft.com/kb/154596/en-us</a> </pre> <br /> 기본적으로는 레지스트리에 아무런 값이 없습니다. <br /> <br /> <img alt='port_reserve_1.png' src='/SysWebRes/bbs/port_reserve_1.png' /> <br /><br /> 하지만, "rpccfg /pe 5050-5070"와 같이 한번이라도 실행해 주면 이렇게 포트 범위가 설정됩니다.<br /> <br /> <img alt='port_reserve_2.png' src='/SysWebRes/bbs/port_reserve_2.png' /> <br /><br /> 참고로, 기본 예약 포트의 범위는 운영체제의 서버/클라이언트 버전에 따라서도 다릅니다. 가령, 클라이언트 운영체제인 Windows 8.1의 경우 다음과 같은 예약 범위를 보여줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > E:\><span style='color: blue; font-weight: bold'>netsh int ipv4 show dynamicport tcp</span> Protocol tcp Dynamic Port Range --------------------------------- Start Port : <span style='color: blue; font-weight: bold'>1025</span> Number of Ports : <span style='color: blue; font-weight: bold'>64510</span> </pre> <br /> <hr style='width: 50%' /><br /> <br /> 다행히 조금 더 공격적인 포트 제외 방법이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > How to reserve a range of ephemeral ports on a computer that is running Windows Server 2003 or Windows 2000 Server ; <a target='tab' href='http://support.microsoft.com/kb/812873'>http://support.microsoft.com/kb/812873</a> You cannot exclude ports by using the ReservedPorts registry key in Windows Server 2008 or in Windows Server 2008 R2 ; <a target='tab' href='http://support.microsoft.com/kb/2665809'>http://support.microsoft.com/kb/2665809</a> </pre> <br /> 가령 윈도우 8.1에서 다음과 같이 실행해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > D:\><span style='color: blue; font-weight: bold'>netsh int ipv4 show excludedportrange protocol=tcp</span> Protocol tcp Port Exclusion Ranges Start Port End Port ---------- -------- 80 80 8012 8013 8023 8025 8090 8093 * - Administered port exclusions. </pre> <br /> 예약된 포트를 알 수 있습니다. 재미있는 것은, IIS 서버의 웹 사이트에 할당한 포트가 기본적으로 포함되어 있다는 것입니다. 따라서, IIS 웹 사이트용 포트는 절대 다른 프로그램에서 사용하는 TCP 소켓과 충돌이 발생하지 않습니다.<br /> <br /> <a name='netsh_delete'></a> 물론, 우리가 원하는 포트를 다음과 같은 명령어로 추가/<a target='tab' href='https://www.sysnet.pe.kr/2/0/12305'>삭제</a>할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\Windows\system32><span style='color: blue; font-weight: bold'>netsh int ipv4 Add excludedportrange protocol=tcp startport=1000 numberofports=5 store=persistent</span> Ok. C:\Windows\system32><span style='color: blue; font-weight: bold'>netsh int ipv4 delete excludedportrange protocol=tcp startport=1000 numberofports=5 store=persistent</span> Ok. </pre> <br /> 따라서, 서버에서 충돌이 발생할 수 있는 범위의 포트가 있다면 이런 식으로 예약해 두는 것이 좋습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1450
(왼쪽의 숫자를 입력해야 합니다.)