Node.js로 WebSocket, TCP, UDP 서버를 만들어서 TCP와 UDP의 특징을 확인해보자

Explanation

즐거운 주말입니다! (사실, 홈 프로텍터라서 평일과 주말의 차이가 없..)
그래도 주말이니까, 오늘은 집에서 쉬면서 뭘 해볼까 고민하다가 TCP / UDP 서버를 만들고 직접 통신을 해보면서 각 프로토콜의 특징을 한번 간단하게 살펴보려고 합니다!

사실 무려, 4년 전에 “https://falsy.me/tcp-udp-서버-공부-1-node와-c로-tcp-udp-서버-만들기/” 이렇게 시도를 한 적이 있었는데요. 그때는 무지해서 (사실 지금도 크게 다르지 않지만..) 서버만 만들고 특징은 직접 구현 해보지 못했더라고요, 이번엔 간단하게라도 그 차이를 확인할 수 있는 환경을 구현해보려 합니다!

전체 인프라 구성은, 직접 값을 제어할 수 있는 클라이언트(브라우저)와 연결하는 WebSocket 서버 그리고 WebSocket 서버를 TCP 서버, UDP 서버와 연결해서 확인해 볼거에요.

작성된 코드는 깃허브에서 모두 확인하실 수 있습니다.
https://github.com/falsy/blog-post-example/tree/main/websocket-tcp-udp

1. TCP Server

간단하게 Node.js로 TCP 서버를 만들고 실행해줍니다.

아주 간단하게 8000번 포트에 서버를 열고 요청이 오면 그 데이터를 그대로 응답하는 코드로 되어 있습니다.

2. UDP Server

Node.js로 UDP 서버도 간단하게 만들고 실행해줍니다.

UDP 서버는 7000번 포트에 실행해 주었습니다.

3. WebSocket Server

이번에는 WebSocket 서버를 만들어줍니다. WebSocket 서버는 ‘ws’를 설치해 주어야 합니다.

짠, 여기서 벌써 한가지 특징이 나왔네요. TCP 통신은 ‘연결 지향 프로토콜’이기 때문에 WebSocket 서버와 TCP Server 가 connect 메서드를 사용하여 연결을 설정해주고 있습니다. 하지만 UDP 서버는 ‘비연결 지향 프로토콜’이기 때문에 따로 연결 설정 과정이 없답니다.

코드는 간단하게 메시지를 수신 받으면 미리 열어둔 TCP 서버와 UDP 서버에 전송하고 있어요.

3. Clinet

클라이언트는 간단하게 Webpack, React를 써서 만들었어요.

짠, 이제 input에 값을 메시지를 입력하고 send 버튼을 누르면 TCP 서버와 UDP 서버에 로그가 찍힌답니다.
저는 “2222” 라고 전송해봤어요.

4. 응답 속도

이제 본격적으로 TCP와 UDP의 차이를 확인해볼까요?

우선 속도를 확인할 수 있도록 WebSocket 서버에서 TCP와 UDP서버에 메시지를 전송하고 응답받는 속도를 확인해 볼게요.

자, 이제 빠르게 메시지를 1, 2, 3 보내봅니다!

짠!

맙소사. TCP가 UDP보다 빠르네요..

생각해보니까, 일반적으로 UDP가 TCP보다 빠르다고 하는 건 다양한 이유가 있지만, 그중에 TCP는 신뢰성 있는 데이터를 보장하기 위해 데이터 전송 전에 동기화 패킷을 주고 받는 검증 절차(3-Way Handshake)가 있기 때문인데, 지금 연결은 WebSocket관 연결하다보니, 처음에 한번 이 절차를 수행하고 연결 후에는 바로 데이터를 전송하기 때문에 TCP가 더 빠른 결과가 나온 거 같아요.

TCP 연결은 통신마다 다시 연결하도록 코드를 수정하고 다시 테스트 해볼게요.

오! 큰 차이는 아니지만 UDP가 조금 더 빨라졌어요. 지금은 아주 데이터가 작고 그리고 로컬 환경에서 TCP는 OS에서 더 많이 최적화가 되어 있어서 그렇고, 실제 서버에서는 아마도 더 차이가 클 거 같아요.

5. 데이터 무결성

TCP의 또 중요한 특징중에 하나는 패킷의 순서 보장인데, 이 패킷의 순서는 애플리케이션단에서는 확인이 어려울 거 같아서, 대신 UDP의 데이터 손실에 대해서 확인을 해보려고 합니다!

TCP라면, 데이터 전송 중 패킷이 손실이 발생하면 손실된 패킷을 다시 전송해서 무결성을 보장했겠죠?!

이전 응답 속도 테스트랑 같이 구현하려니 코드가 좀 복잡해져서 코드를 분리해서 따로 만들었어요.

예전 기억에, MacOS에서 UDP 패킷이 최대 9KB 까지만 가능하다고 본 기억이 있어서 청크의 크기는 적당히 작은 6KB로 짤라서 보내도록 했어요.

WebSocket 서버에서도 동일하게 UDP 서버로 6KB 씩 나눠서 보내도록 했어요.

마지막으로 클라이언트 코드입니다.

이미지를 업로드하면, 업로드한 이미지를 FileReader를 사용해서 출력하고 이미지를 WebSocket 서버로 보냅니다. 그리고 응답받은 데이터는 6KB씩 짤려서 오기 때문에, 간단한 구현을 위해서 setTimeout으로 1초 안에 새로운 데이터가 안오면 모든 데이터가 온걸로 간주하고 Blob으로 이미지를 합치도록 했습니다.

바로 테스트를 해보면!

짜잔! 패킷이 손실되서 이미지가 깨져서 돌아오는 것을 확인할 수 있답니다.

분명.. 일요일에 시작했는데, 월요일 아침인 건 안 비밀..