NAT을 사용하게 되면 내부에 연결된 host들은 Public IP가 아닌 Private IP를 사용하게 된다.
다른 LAN에서는 Private IP끼리 통신은 불가능하므로 홀펀칭이라고 하는 방식을 사용해 통신을 하게 된다.
간단히 3단계로 정리하자면
1. Private IP로 전달해본다.
2. 안되면 Public IP로 전달해본다.
3. 그래도 안되면 Realy Server로 패킷 전달을 요청한다. (TCP/UDP)
각자의 클라이언트는 gethostname을 통해 본인의 IP를 확인한다.
(이때 확인되는 IP는 Private인지 Public인지 스스로는 알 수 없다.)
그래서 다른 외부에 있는 서버로 데이터를 보내면 외부 서버는 클라이언트의 Public IP와 Port를 확인하게 되고, 해당 정보를 클라이언트에게 되돌려준다. (TCP는 연결 단계가 많으니 UDP로 하는게 간편하지만 Loss 가능성)
클라이언트는 그럼 Private IP와 Public IP를 모두 손에 넣었으니, 게임 서버에 로그인할 때 둘 다 알려주면 된다.
그럼 게임 서버는 클라이언트끼리 P2P 통신이 필요할 때 해당 클라이언트들의 IP 목록을 Broadcast 해주면 서로가 서로의 정보를 알 수 있게 되고, 해당 정보로 위의 3단계를 통해 열심히 데이터를 주고받으면 된다.
다만 해당 통신 방법 같은 경우는 최초 연결이 한 번으로 되지 않을 수 있으므로 여러 번 시도해보고, 최초 성공 이후에도 일정 시간 통신이 없을 경우 Port가 막힐 수 있으므로 heartbeat 같은 일정 주기의 데이터를 쏴주면 좋을 것 같다.
출처의 댓글 중 유익한 내용
1. 3단계 중 3번째는 클라이언트들이 모두 Realy Server에 연결을 하고 있어야 한다
2. gethostname으로 얻어지는 ip는 NIC가 여러 개일 때 선택이 어려우므로, 서버에 연결된 이후 받아오는 sockaddr의 정보를 확인할 것. (또는 getpeername / getsockname 함수 사용으로 얻어지는 sockaddr)
4. 동일한 NAT에서 두 클라이언트가 접속했을 때는 다른 클라이언트들이 동일 NAT 내에 있는 두 클라이언트를 어떻게 구분할 수 있느냐?? => NAPT(https://5kyc1ad.tistory.com/254)
출처: https://gamedevforever.com/47?category=387043