life is egg

[나만무] 웹게임 TCP에서 UDP로 전환 과정 본문

sw정글

[나만무] 웹게임 TCP에서 UDP로 전환 과정

삶은계란진재혁 2024. 7. 23. 09:33

intro..

정글의 마지막과정 중 나만의 무기만들기를 진행하면서 겪은 기술적 챌린지 또는 고민 중 웹게임의 통신 방식을

TCP에서 UDP 프로토콜을 변경한 과정이다.

 

Web Socket은 TCP기반..!

클라이언트 - 서버 구조 기반의 웹게임을 만드는데 이제 캐릭터의 좌표나 공격판정등..을 웹소켓을 이용해서 구현하고 있었다.

그러던 도중 Web Socket은 TCP 기반이라는 사실을 알게된 후로... UDP로 바꿔야지 란 생각이 머리 속을 가득 채웠다.

 

UDP로 바꿔야 될 이유를 생각해보자면

  • 프론트단에서  부드러운 움직임을 구현해주기 위해서 주고받은 좌표와 좌표사이 움직임을 선형보간으로 보정해주기 시작했는데...
  • 그러니 패킷이 좀 소실되더라도 보정을 해주니까  TCP보다 UDP가 더 적합했다.

 

Geckos.io

그렇게 웹에서 UDP로 소켓 통신을 할 방법이 뭐가 있을까 찾다 찾다 나온게 바로 Geckos.io다 완전 생소한 라이브러리라서 걱정했는데

Node data channel 을 이용해서 소켓통신을 UDP로 하게 해주는 라이브러리이다 

Node data channel 은.. WebRTC data channel의 기능을 Node.s 환경에 통합한것.. 이를 통해 Node.js 서버와 클라이언트 간 실시간으로 데이터를 교환 할 수 있음 ..!

 

CJS  to EMS

일단.. geckos.io 를 node-datachannel 에서 쓰려면 일단 문제가 우리 백서버는 기본적으로 CJS 방식을 고수하고있었는데

EMS 방식으로 전환을 해야지 사용 할 수있었다.. 여기서 부터 첫번째 고비였긴했는데 대부분의 라이브러리들은 CJS 와 EMS 방식 모두다 지원하게 해주는데 geckos.io는 noly ESM방식을 사용해야하기 때문에.. 우리의 프로젝트도 거기에 맞춰서 싹다 방식을 변경해줘야했다.

그래도 변경하고 보니 똑같이 내가 improt {} from을 선언햇어도 빌드파일을 살펴보면 CJS 일때는 다시 require 로 바뀌는데, EMS 는 빌빌드해도 빌드 파일을 보면 improt {} from 형식으로 되는걸 볼 수있엇다.. ESM 방식은 비동기적 로딩이기 때문에.. 브라우저 환경과의 호환성을 높여준다는데..흠.!

 

geckos.io 의 강력한 장점 중의 하나라면 socket.io와 사용 방법이 상당이 유사해서 socket.io로 웹소켓을 구현해봤다면 쉽게 geckos.io를 사용 할 수 있다는 점이다 ..!

 

STUN (Session Traversal Utilities for NAT)

뭔가 하라는데로 했는데.. 로컬에서는 서로 클라이언트와 연결도 성공하고 데이터도 잘 주고 받는데...

ec2로 배포해서 확인해보면 안된다고한다..

이유가 뭘까 .. !  바로 NAT(Network Address Translation..) 환경에서는 내부 네트워크의 IP 주소를 외부 네트워크에서 보이는 공용 IP 주소로 변환해주는 역할을 하는데 . 이때문에 외부에서 내부 네트워크로 들어오는 연결을 설정하는 것이 어려워짐..

이러한 연결의 어려움을 해결하기 위해서 등장한 것이 바로 STUN 이다 

 

즉 클라이언트가  STUN 서버로 요청을 보내주고 요청을 받은 STUN 서버는 요청이 온 클라의 공용 IP 주소와 포트를 알 수 있게 응답해주는 역할..!  

 

우리는 구글에서 제공해주는 STUN 서버를 이용했다.

요런 느낌이다!

 

 

클라이언트에서 소켓연결 종료시 disconnect() 호출이 안된됨..!

 

이후 여러가지 테스트를 하다가 발견한 오류이다. geckos.io 로 바꿔서 발생한 오류인 줄 알 고 있었지만 일반적인 Socket.io를 사용 했더라도 발생 했을 오류인 듯하다. 무슨 오류냐 하면 바로.. 소켓 연결이 종료되어서 캐릭터가 없어져야하는데 대략 20초간 맵안에 남아있는 상황이였다.

 

소켓연결을 종료 하는 3가지 상황 중 한 가지 상황에서만 오류가 나타났다.

  • 새로고침 
  • 뒤로가기
  • 페이지종료

위의 3가지 상황중 페이지를 바로 종료하는 상황에서 오류가  발생했다.

웹소켓을 사용하는 상황에서 클라이언트가 연결을 종료하려고 할 때, 일반적으로 WebSocket 연결을 닫으면서 disconnect() 를 서버로 보네는데 .. . 하지만 페이지를 새로고침하거나 뒤로 가기할 때는 beforeunload 이벤트나 unload 이벤트를 이용해서 연결을 정리할 수 있는 기회가 있다고한다.

하지만 페이지를 그냥 닫아버릴 경우(즉, 브라우저 탭을 닫거나 브라우저 자체를 종료하는 경우), 클라이언트 측의 자바스크립트코드가 실행될 기회를 갖지 못할 수 있다는것..!

이는 브라우저가 페이지를 닫는 동작에 대해 즉각적으로 반응하며, 클라이언트 측 코드가 이를 처리할 시간을 주지 않기 때문입니다. 이러한 경우, 자바스크립트가 종료 이벤트를 처리할 기회가 없기 때문에, 웹소켓 연결을 정상적으로 종료하지 못하고 "디스커넥트" 메시지를 서버로 보내지 못할 수 있습니다.

이를 해결하기 위한 방법으로는 서버 측에서 클라이언트의 비정상 종료를 감지하기 위해 "ping-pong" 메커니즘을 이용 하기로 했고, upd 특성상 ping-pong을 못 할 수 도 있다고 생각해서 3번까지 pong 응답이 없다면 연결이 종료된걸 감지하고 캐릭터를 지워주는 방식으로 변경했다.

 

 

결과

왼쪽이 TCP 방식이고 오른쪽이 UDP 방식인데  오른쪽이 이미지 압축하면 화질이 너무 깨지는데 어떻게 더 용량을 줄일방법이 없어서 ..

그래도 적용전보다는 훨씬 부드러워졌다



Comments