사용 가능한 포트번호 구하기



Intro

 저는 요즘 네트워크와 관련된 모듈을 개발하고 있습니다.
.NET 플랫폼 위에서 개발(C#)을 하고 있지요.

그렇게 개발을 하고 있는데, http 서비스를 하는 기능이 필요했습니다.
서비스가 만약 1개라면, 임의의 포트를 정해서 돌리면 되겠지요.
하지만 서비스는 몇 개든지 동적으로 요청에 의해 생성되어야만 했습니다. 물론 한계는 있겠지요.
아시다시피 port 번호는 Data type 이 int 형이니까요.

아무튼 이런 이유로, 현재 시스템에서 사용하고 있지 않는 '유효한 포트' 번호를 알아내야 했습니다.



Content

처음 구글링을 시작했습니다.
"당연히, .없는게 없다는 NET 인데 사용 가능한 포트번호를 알려주는 함수가 있을거야."
라는 생각 때문이었죠.

그런데 생각만큼 쉽게 찾을 수 없었습니다.
Socket 을 생성해서 loopback 으로 연결을 시도해서, 가능하면 사용 불가. 불가능하면 사용가능.
이런식으로 찾아내야 한다는 이야기부터,, Socket.Select 를 이용하라는 방법까지...

모두 명확하지 않고, 찝찝하기 그지없으며, 함수가 실행되는데까지 소요되는 시간도 불명확 했습니다.
도저희 그런 함수를 사용할 수 없다라는 결론에 달했죠.

그래서 생각을 바꾸었습니다.
"어차피 포트 번호의 범위는 정해져 있고, 사용 가능한 포트라는건 현재 사용하고 있는 포트가 아닌 것이다!"
즉, 역설적으로 생각하기 시작했습니다.

1 ~ 65535 범위 안에서 '현재 사용하고 있는 포트' 를 제거하면, 나머지는 사용 가능한 포트!

그리고 코드를 짜기 시작했지요.
참, 저는 TCP 서비스를 하려는 것이기 때문에, 현재 사용중인 TCP 포트만 걸러내면 되는 상황이었습니다.



Code


[사용 가능한 TCP 포트를 구하는 코드]


private static object IdlePortLock = new object();

public static int GetIdleTCPPort(int minimum) {
    // 유효 포트 범위를 벗어나는 경우 Exception
    if(0 >= minimum || minimum > 65535) { throw new Exception(); }

    // 사용하지 않는 포트를 구하려면 lock 은 필수!
    lock (GenaListener.IdlePortLock) {
        // 사용중인 포트번호 리스트
        List<int> usedPortList = new List<int>();

        // active 중인 TCP 포트 리스트
        TcpConnectionInformation[] connections = 
IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections();
        foreach (TcpConnectionInformation info in connections) {
            usedPortList.Add(info.LocalEndPoint.Port);
        }

        // listen 중인 TCP 포트 리스트
        IPEndPoint[] endPoints = 
            IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
        foreach (IPEndPoint point in endPoints) {
            usedPortList.Add(point.Port);
        }

        // 사용중인 포트가 하나도 없으면 최소포트 리턴
        if (usedPortList.Count <= 0) { return minimum; }
        usedPortList.Sort();  // 사용중인 포트를 번호순으로 정렬
         usedPortList = usedPortList.Distinct<int>().ToList(); // 중복값 제거


        // 현재 사용중인 포트를 임시로 저장
        int temp = usedPortList[0];
        foreach (int port in usedPortList) {
            // 유효한 포트가 없다면 Exception (포트범위 초과)
            if (temp > 65535) { throw new Exception(); }

            // minimum 보다 작은 포트면 pass
            if (temp < minimum) { temp = port + 1; continue; }

            // minimum 포트보다 크면, 사용중인지 아닌지 체크
            if (temp == port) { temp++; } else { return temp; }
        }

        return minimum; // 사실 조건에 만족하는 포트가 없다면 Exception 을 throw 해야 됨
    }
}





Epilogue


코드를 잘 보시면 조금 부족한 부분이 있습니다.
무엇일까요? 생각해 보세요.

1. 마지막 주석에 나와 있듯이 조건에 만족하는 포트가 없다면,  minimum 을 return 할게 아니라, Exception 을 throw 해야 맞겠지요.

2. Thread 에 Safe 하지 않습니다.
 - 만약 두개 이상의 thread 가 위 함수를 호출했다고 가정해 봅시다.
  1번 Thread 가 함수 수행결과로 80번을 리턴 받았고, 그 다음 2번 Thread 가 연이어 함수를 수행한다면?
  두 Thread 모두 동일한 포트를 리턴받을 것입니다.
  즉, 함수 수행결과 리턴된 포트를 사용하기 전에, 함수를 또 실행하면 같은 포트를 할당받는다는 것입니다.
  
생각해봐도 2번 문제의 해결은 어떻게 해야 할지 잘 모르겠네요.
좋은 생각이 있으신 분은 답변달아 주시면 감사하겠습니다 ^-^

오늘은 여기서 블로깅을 마치겠습니다. 감사합니다~

p.s - 버그가 있어서 수정하였습니다.(중복값 제거 부분)


  • 가이 2013.06.07 11:44

    글 잘보았습니다.
    아마도 2번 문제는 이 함수에서 해결할 부분이 아니라 포트를 받아서 사용하는 부분에서 해야할 문제 같습니다.
    할당받은 포트로 접속해보고 실패한다며 재시도 하도록 하는 것이 좋지 않을까 생각되네요.