TCP 소켓을 이용하여 1:1연결을 해야 하는 상황이 있어 TCP hole-punching을 해주는 라이브러리를 개발하게 되었다. 연구하면서 알게 된 내용을 정리하려고 한다.
로컬 망 안에 속한 peer끼리의 연결은 알다시피 매우 간단하다. 하지만 연결하려는 peer가 외부 네트워크에 있고 모두 NAT에 의해 IP가 변조된다면 어떻게 연결할까..
당연하게도 TCP연결을 하려면 먼저 상대방의 IP를 알아야 한다.
우리는 모두 NAT뒤에있는 환경이라고 가정하면 나와 상대의 IP를 모두 알아내야하는데 어떻게 알아내는가..
NAT뒤에 있는 상대의 IP도 알아내는 방법이 있다. (정확히는 상대방이 사용하는 NAT의 공인IP를 알아내는것.)
바로 STUN 서버를 활용하는 것인데..
지금부터 이야기하는 내용은 A와 B가 동시에 진행한다.
STUN의 역할은 A, B의 공인IP를 알려주는 역할이다.
예를들어 A가 Discovery 동작으로 STUN서버에게 자신의 공인IP를 알려달라는 요청을 보내면 STUN서버는 반환 패킷에 Mapped-address(A의 공인IP)를 담아 보내준다.
이렇게되면 A, B는 자신들이 사용하고있는 공인IP를 알 수 있게된다.
이제 A,B는 다른 서비스를(릴레이 서버같은) 사용하여 서로의 공인IP와 사설IP 등등을 교환하고 서로 상대방의 주소에 연결을 시도할 수 있게된다.
- 주의 -
앞으로 사용하는 소켓의 source 주소는 Discovery때 사용한 ip, port와 B로 접속할때 사용하는 ip, port가 같아야한다.
NAT의 IP주소 매핑 패턴 때문인데..
이 NAT 매핑 패턴때문에 서로의 IP를 알더라도 접속이 불가능할 수 있다.
Discovery를 이용하여 test1,2,3단계를 진행하여 내가 사용하고있는 NAT의 IP매핑 패턴을 알아내는 방법이 있지만, 사실 TCP는 NAT의 매핑 패턴을 알아내더라도 연결이 불가능한 패턴을 가능하도록 할순 없다.
그렇기 때문에 NAT 패턴은 생략하고, 교환된 서로의 공인IP로 일단 무식하게 연결을 시도한다면, 접속이 될수도 안될수도 있다.
연결 시도 과정에도 방법이 있는데
일단 TCP연결을 하려면 보통은 server/client 방식으로 연결을 한다.
지금 우리는 A, B가 동시에 연결을 시도하려고 하기때문에 누가 서버고 누가 클라이언트인지 정해져있는 것은 없다. relay를 통해 교환하려면 할순 있지만.
간단한 방법들이 있는데 모두 시도하는것이다.
일단 tcp connect하는 방법으로 꼭 listen상태의 소켓에 접속하지 않아도 된다.
tcp simultaneous open 이라는 방법이 있는데.
양쪽의 소켓이 일정시간안에 서로 connect를 시도하면 연결이 되는것이다.
Discovery동작때 사용했던 source주소를 가지고 소켓 listening하고 있는다.
어찌됐든 윗단계에서 connect를 시도할텐데 일단 열어두면 된다.
연결시도는 상대의 사설IP 목록이나 공인IP목록으로 접속을 동시다발적으로 시도하게된다. 이때 연결이 성공하는 소켓은 여러소켓이 등장할텐데..
나는 단순하게 여러소켓중 가장 빠르게 접속된 소켓을 선택하여 사용하도록 했다.
WebRTC의 경우 연결 후보자에 우선순위가 있어서 우선순위대로 시도하는것 같다.