본문으로 바로가기
728x90
반응형
728x170

P2P, Relay 서버 방식에 대해 설명했을 때 IPv4가 갯수가 한정적이기 떄문에 모든 컴퓨터에 고정IP를 둘 수 없어 맨 앞에 있는 컴퓨터만 Pubic IP만 가지고 있으면 얘 한테 오는 패킷을 분배를 할 수 있었습니다.

그래서 이 맨앞에 있는 컴퓨터 이하에 있는 컴퓨터들은 가짜IP를 가지고 있어도 통신을 할 수 있었고, 이를 통해 자원고갈 문제를 해결할 수 있었습니다.

 

이 부분을 좀 더 자세히 보죠.

패킷이 안에서 바깥으로 나갈 때는 가능하지만 바깥에서 안으로 들어가는 것은 불가능 합니다.

안에서 바깥으로 나갈 때를 자세히 살펴보죠.

안에서 바깥으로 나갈 때의 목적지를 Naver라고 쳐보죠. Naver가 고정IP이기 때문에 Naver의 서브IP는 변하지 않습니다.

내 컴퓨터의 IP가 10.2.4.6이고 게이트웨이의 고정IP가 24.1.25.6라고 쳤을 때 내가 인터넷 브라우저에서 Naver.com을 켰을 때 패킷이 Naver IP로 나가게 되는데 이 패킷이 나갈 때 게이트웨이가 하는 일이 뭐냐면 나가는 패킷이 10.2.4.6의 특정 포트로 나갔을텐데 그 포트번호를 256이라고 가정해서 나가는 출발위치가 있고, Naver의 Public IP라는 목적위치가 있을 것입니다.

이 때 Naver의 Public IP는 31.4.22.4라고 가정해 봅시다. 그러면 게이트웨이는 Private IP의 특정 포트에서 출발한 패킷이 Naver의 Public IP의 특정 포트에 나가는경우에 거기에 대한 Mapping Table을 하나 만들어 줍니다.

출발지와 목적지를 적고, 그것을 몇번 포트에 바인딩 할 지 지정해줍니다.

 

이 부분이 중요해서 다시 한번 보자면 내 안쪽에 있는 패킷이 바깥쪽으로 나갈 때 Mapping하나 만드는데 이 때 나의 특정포트를 253번이라고 가정하고, 253번 포트에 출발지 목적지를 다음과 같이 적어줍니다.

PORT 출발지 목적지
253 10.2.4.6 31.4.22.4(Naver)

이렇게 하나의 Map Table을 패킷이 나갈 때 만듭니다.

그랬을 때 Naver에서 요청을 받았으면 응답을 보내주는데 응답을 보내줄 때 지금 게이트웨이가 253번 포트로 나갔는데 이 포트는 출발지의 포트와는 다릅니다.
출발지가 1000번 포트가 될 수 있고, 10000번 포트가 될 수 있습니다. 그것과 상관없이 내가 이 게이트웨이 포트에서 비어있는 포트 아무거나 할당합니다.

Naver입장에서는 게이트웨이 안에 있는 내 컴퓨터의 IP는 모릅니다. Private IP가 10.2.4.6에 출발했는지 알 수 없습니다.
Naver가 알 수 있는 것은 게이트웨이의 Public IP와 게이트웨이가 할당한 포트번호만 알 수 있습니다.

 

그래서 이 패킷은 출발지가 24.1.25.6:253으로 나간다. 실제 출발한 위치가 아닙니다.

그러니까 게이트웨이가 매핑해서 내가 24.1.25.6:253으로 출발했다고 치고 나가는 것입니다.

이제 Naver가 응답을 해주어야 하는데 Naver가 24.1.25.6:253 밖에 모르기 때문에 여기에 패킷을 쏴줍니다.

여기에 패킷을 쏘면 게이트웨이는 패킷이 출발할 때 만든 Map Table을 봅니다.

그러면 포트가 253번이고, 패킷이 10.2.4.6에 바인딩 되어있는걸 보고 여기에서 출발한 패킷의 응답이라는 것을 알 수 있기 때문에 여기에 패킷을 쏴줍니다.

 

여기까지 이해하셨다면 이 상황에서 왜 P2P가 안되는지 알 수 있을 겁니다.

이렇게 내 PC가 있고, 상대방 클라이언트도 저렇게 있다고 칠 때, 커넥션을 맺을려면 Request 패킷이 가야 하는데 게이트웨이 뒤에 있는 컴퓨터 입장에서 보면 내가 묶여있는 게이트웨이의 Public IP가 몇번인지, 내가 몇 번 포트에 바인딩 될 지는 알 수가 없습니다.


그렇기 때문에 상대방한테 내 게이트웨이의 Public IP, 게이트웨이가 바인딩할 포트 번호를 알 수 없기 때문에 어디에 패킷을 쏴야 하는지 상대방에게 알려 줄 수 없고, 상대방도 마찬가지로 알려줄 수 없습니다. 그렇기 때문에 서로 패킷이 나가기만 되지, 들어오는 것이 안되기 때문에 서로 통신할 수 없는 상황이 됩니다.

그래서 이것을 어떻게 하냐면 나가기는 되기 때문에 고정IP(Public IP)를 가진 서버 하나를 두고 양 쪽 클라이언트 모두 이 서버에겐 패킷을 쏠 수 있습니다.

그 때 그 서버 입장에서는 이 패킷이 어느 Public IP에서 어떤 포트에 바인딩 되었는지는 알 수 있습니다.
게이트웨이로 패킷이 출발했을 때 나는 Private IP, Private Port인데 이것을 매핑합니다. (이것을 포트포워딩이라고 합니다.)

포트포워딩으로 특정 포트에다 매핑시켜서 거기를 출발점이라해서 Server에 쏴줍니다.
이 때 서버는 고정된 IP이기 때문에 패킷이 갈 수 있습니다.

서버입장에서는 패킷을 받으면 출발지가 적혀있는데 해당 게이트웨이의 Public IP의 해당 게이트웨이가 해당 클라이언트에게 매핑했던 포트번호가 적혀있습니다.

이것은 상대방도 마찬가지 입니다. 그래서 나의 실질적인 Private IP와 Port번호는 모르지만 내가 묶여있는 게이트웨이의 Public Ip와 Port번호는 알 수 있고, 상대방것도 알 수 있습니다.

그렇게 되면 어떻게 되냐면 이것을  서버입장에서 상대방에게 온 정보를 A, 나에게 온 정보를 B라고 가정했을 때, 상대방 클라이언트를 C1, 내 클라이언트르 C2라고 해보죠.

그러면 TCP연결이기 때문에 서버는 A에게 패킷을 보낼 수 있습니다. 게이트웨이 입장에서는 Mapping Table로 매핑을 해놓았기 때문에 C1에게 B정보를 전달해줍니다.

그니까 C2 게이트웨이의 Public IP와 Port번호를 C1에게 전달 해줍니다. 그리고 C2에게는 C1의 Public IP와 Port번호를 C1에게 전달 해줍니다.

그 다음에는 C1은 C2에게 서버가 알려줬던 Public IP와 Port번호로 패킷을 쏩니다.

그리고 C2는 마찬가지로 C1에게 서버가 알려줬던 Public IP와 Port번호로 패킷을 쏩니다.

아까 Naver에게 전송했을 때와 상황이 다른데 naver는 목적지에서 출발한 패킷이 똑같은 위치로 들어가는 것인데 지금은 내 출발지가 서버로 출발했지만 패킷은 다른곳에서 출발한 패킷이 같은 포트번호로 들어오는 것입니다.
이렇게 되었을 때 게이트웨이는 마음대로 판단합니다.

같은 포트번호로 들어왔기 때문에 게이트웨이는 이미 Mapping Table에 적어놨을 것입니다.
그것을 보고 바로 바인딩 된 포트에 보내줄 수 도 있고, 출발지와 다른 것을 보고 안 보낼 수도 있게 됩니다.

이렇게 게이트웨이의 마음인데, 대체로는 그냥 보내주는 경우가 많습니다.

이렇게 그냥 보내주었을 경우에 이것을 '홀펀칭'이라고 합니다.

아까 바인딩하는 것과 매핑하는 것과 보내줄 지 말지를 정하는건 게이트웨이 마음인데 이러한 방식을 'NAT'이라고 합니다.

포트 포워딩을 해서 IP를 변조했다 해서 NAT장비라고 합니다. 
그러니까 안에 들어있는 클라이언트에서 출발한 패킷이 Private IP를 숨기고 Public IP로 가서 특정포트에 바인딩하는 상태에서 보내준다고 해서 NAT장비라 합니다.

NAT장비에는 여러가지가 있는데 게이트웨이도 있고, 라우터도 있고, 스위치도 있고, 공유기, 등등이 있습니다.
그리고 NAT 장비가 중첩으로 들어갈 수도 있습니다.
예를 들면 LG에서 제공하는 ISP안에서 중간 라우터를 거쳐서(안 거칠 수도 있고), 우리집에서 쓰는 공유기를 거쳐서(없으면 안 거치고) 우리집까지 옵니다.

그러니까 NAT장비를 여러 단계를 거쳐서 인터넷 환경으로 나아가는 상황이기 때문에 각각에서 IP가 변경되는 것입니다.

그래서 내 컴퓨터에서 IP를 조회했을 때 진짜 바깥에서 공개되는 IP인 줄 알지만 사실은 공유기가 할당해준 Private IP인 것입니다.
라우터 입장에서도 분명히 자신이 가진 IP가 Public IP라고 생각할 수 있겠지만 사실은 더 큰 NAT장비(LG ISP)에서 할당해준 Private IP 입니다.

그래서 어떤 것의 뒤에 들어있는 상황이기 때문에 이것을 아무도 확신할 수 없습니다.
그런데 대체로 한번 바인딩 된 포트로 패킷을 쏘면 그 유저에게 가더라는 것이 있지만 100% 확신할 수 없는 상황이고,
그렇게 갔으면 홀이 뚫린 것이라 이것을 홀펀칭이라 하는데 이것이 뚫릴지 말지는 NAT장비 마음이기 때문에 가정할 수는 없습니다.

그래서 어떤 경우에는 포트포워딩이 300번에 포트포워딩을 하면 그 다음 패킷은 301번에 되더라, 어떤 것은 300번에 포트포워딩을 하면 302번에 되더라, 어떤 것은 300번에 포트포워딩을 하면 400번에 되더라해서 1증가 시켜서 날려보고, 2증가 시켜서 날려보고, 100증가 시켜서 여러가지의 시도를 해서 뚫어지면 홀이 뚫리지만 보통은 그렇게해서 뚫는것보다 한번 해서 안되면 마는 경우가 많습니다.

 

다시 정리하면 NAT가 있고, NAT장비 뒤에 들어가 있으면 내 IP가 감추어 집니다.

그랬을 때 외부에 공개된 IP를 가진 서버가 하는 일은 그 서버에게 오는 바인딩 된 포인트를 찾아서 상대방에게 알려주는데 이 때의 서버를 STUN Server라고 합니다.

STUN Server를 통해서 내가 패킷이 어떤 포인트에 바인딩 되어 있는지 알 수 있는데 거기에 패킷을 쏘게 되고, 그 때 뚫어줄지 말지는 NAT장비 마음 입니다.

뚫리면 홀이 뚫린것이기 때문에 거기에 계속 통신하면 되고, 안뚫리면 Relay하는 방법 밖에는 없습니다.

마찬가지로 Public IP를 가지고 있는 고정된 서버를 하나 만들어 놓고 그 서버에게 패킷을 쏴주면 그 서버가 해당 클라이언트에게 전달해주고, 다시 그 클라이언트가 서버에게 패킷을 쏘면 서버가 게이트웨이에게 전달해주는 고정된 IP를 가진 Relay Server를 통해 전달 할 수 밖에 없습니다.

이것을 TURN Server라고 합니다.

 

그래서 이런 방식을 사용하는 것들 중에 WebRTC 같은 프로토콜이 STUN Server를 통해 홀펀칭을 시도해보고, 안되면 TURN Server를 통해 돌리는 일을 하게 됩니다.

 

지금까지 정리를 해보자면 TCP와 UDP가 있는데 TCP가 좋은데 순서,연결보장, 등 다 해주는데 속도 때문에 UDP를 쓰고, UDP가 속도만 빠르고 TCP가 보장해주는 것들을 안해줘서 Reliable UDP를 쓴다고 했었습니다.

그리고 지금 봤던 홀펀칭도 UDP를 기반하는 기술입니다. 그래서 예전에는 Reliable UDP와 홀펀칭이 구현하기 까다로웠기 때문에 소스코드도 구하기 힘들었었습니다.

요즘에는 공개 라이브러리도 많기 때문에 이 둘은 더 이상 비밀스러운 기술들은 아니게 되었고, 아까 얘기했던 WebRTC처럼 표준 프로토콜에서도 이것을 사용하고 있다. 이것은 기본적으로 이 둘을 지원하고 있습니다.

그리고 표준은 안되었지만 QUIC라는 프로토콜도 이 둘을 지원해주고 있습니다. 그래서 더 이상 비밀스러운 기술은 아닙니다.

 

 

ckdqja135/Typescript-restful-starter

Contribute to ckdqja135/Typescript-restful-starter development by creating an account on GitHub.

github.com

 

728x90
반응형
그리드형

댓글을 달아 주세요