본문 바로가기

알림

UDP 브로드캐스트의 재발견: 네트워크 브로드캐스팅 라이브러리 만들기

혼자 진행해오던 프로젝트를 공개하기 전에, 프로젝트의 내용을 정리하려는 의도로 작성된 글입니다. 내용이 보강되거나 변경될 소지도 있으니 갱신일을 확인해 주시면 감사하겠습니다.

- 작성일 2012/02/02
- 갱신일 2012/02/03



#.
오늘날 프로그래머가 다루는 기술 분야는 매우 다양하고, 실제 프로그래밍에 있어서 프로그래머가 저수준(수준이 낮다는 뜻이 아니라, 추상화가 덜 되었다는 의미로)의 기술 주제를 다루는 일도 드문 일이 되었다. 네트워크 프로그래밍의 경우에도 마찬가지여서 인터넷을 통해 데이터를 주고받거나 하는 작업에 소켓을 직접 사용해서 프로그래밍하는 경우는 흔치 않은 일이 되었고, HTTP 기반으로 하는 네트워크 프로그래밍이 유행하게 되었다. HTTP를 통한 네트워크 프로그래밍은 이해하기 쉽고, 이러한 프로그래밍 작업을 쉽게 해 주는 라이브러리 또한 도처에 널려 있는 것을 볼 수 있다. 

그러다보니 TCP니 UDP니 하는 것을 다룰 일도 거의 없어졌고, 요즘에는 대부분의 응용 프로그래머가 네트워크에서의 데이터의 전송은 두 개의 특정 호스트가 1:1로 통신하는 것(=유니캐스트Unicast)이 기본적인 것이라고 인식하고 있는 것 같다. 그렇지만 이러한 1:1 통신은 TCP 프로토콜의 특징이며, 이는 HTTP 프로토콜이 TCP 프로토콜을 기반으로 하고 있기 때문에 TCP 프로토콜과 마찬가지의 특징을 갖고 있는 것 뿐이다.

혹시라도 UDP 프로토콜을 아직 다뤄보지 못한 프로그래머를 위해서 짧게 설명하자면, UDP에서는 유니캐스트 외에도 멀티캐스트(Multicast)와 브로드캐스트(Broadcast) 기능을 제공한다. 이는 그림으로 나타내면 다음과 같다:


 

멀티캐스트,  브로드캐스트


멀티캐스트는 자신이 접속한 네트워크 내의 여러 호스트에게 패킷을 전송하는 것이고, 브로드캐스트는 자신이 접속한 네트워크 내의 모든 호스트에게 패킷을 전송하는 것이다. (이 글에서는 브로드캐스트를 다루고 있는데, 특별한 이유가 있어서기보다는 ... 멀티캐스트보다 브로드캐스트가 간단하기 때문이다)

실제 브로드캐스트로 UDP 패킷을 전송하기 위해서 유니캐스트와 다르게 작업해야 할 부분은 별로 없으며, 패킷을 전송할 IP 주소만 올바로 지정하면 된다. 단순한 상황을 하나 가정해보자. 만약 무선 공유기를 사용하고 있고, 무선 공유기에서 할당받은 IP가 192.168.0.4라고 가정하면, 단순히 192.168.0.255라는 IP 주소를 목적지로 UDP 패킷을 송신하면 된다. 그러면 위에 보인 브로드캐스트의 그림처럼, 해당 무선 공유기에 연결되어 있는 모든 네트워크 기기에 UDP 패킷이 전달된다.


#.
이러한 브로드캐스트는 개념만 알고 있다면 상당히 많은 부분에 응용할 수 있다. 게임을 예로 든다면, 브로드캐스팅을 통해 자신의 데이터를 자신이 접속한 네트워크의 모든 호스트에 전송함으로써, 상대방의 IP를 몰라도 게임을 실행한 호스트를 찾아내는 기능을 구현할 수 있다. (스타크래프트의 멀티플레이 게임을 생각해 보자) 필자는 개인적으로 이러한 "발견Discovery" 기능을 UDP의 가장 강력한 특징 중의 하나라고 생각하고 있다.

물론 이러한 UDP의 특징을 응용한 프로토콜이 없는 것은 아니다. 대표적인 것으로 애플의 Bonjour 같은 것이 있다.



애플 홈페이지의 Bonjour 기술 문서를 읽어보면, IP 네트워크에서 서비스를 공개/발견(Publish/Discovery)하기 위한 용도로 사용할 수 있다고 나와 있다. (자세한 내용은 http://en.wikipedia.org/wiki/Bonjour_(software)을 참고하자)



#.
최근까지 필자는 UDP의 이러한 특징을 활용한 프로그램을 만들어오고 있다. 하나는 미디어를 로컬 네트워크에서 배포/구독할 수 있는 솔루션이며(Mug 서비스라고 이름붙였다), 또 하나는 로컬 네트워크를 통해 서로 동기화가 가능한 모바일용 야광봉/전광판 앱이다. (이 블로그 글을 읽고 있다면 이미 알고 있겠지만, BITNA라는 이름을 갖고 있다)

이 프로그램들의 초기 구조를 설계하는 단계에서, 어쨌든 데이터를 서로 주고받아야 하는 네트워크 프로그램이다보니 자체 프로토콜을 설계하는 작업이 필요했다. BITNA 앱의 경우 처음에는 자체 프로토콜(이라기보다는 패킷의 데이터 구조)을 설계하여 적용했었지만, 얼마 못가 이런 저런 문제가 많다는 것을 알게 되었다. 가장 두드러진 문제는 프로토콜 설계 자체의 유연성이었는데, 프로토콜에 기능을 추가하기 위해서 데이터를 추가하면 추가된 패킷 구조에 따른 처리 로직을 계속해서 재검토해야하는 상황이 반복되었다.

위에서 이야기한 Bonjour를 사용할까 하는 생각도 있었으나, 필자가 만드는 프로그램은 멀티 플랫폼을 지향하고 있었다. Bonjour는 오픈소스이고 윈도우나 리눅스용 SDK도 제공되지만, 맥을 제외하면 Bonjour 서비스 자체는 따로 설치해야만 했다. (만약 대상 플랫폼이 맥/iOS 전용이라면 Bonjour도 나쁘진 않다) 그러다보니 차라리 좀 더 단순하고 멍청한(Simple & Stupid, ‘이해하기 쉬운’이란 의미로 받아들이자) 라이브러리를 만들어 보자는 생각이 들었다. HTTP 프로토콜처럼 쉽게 사용할 수 있는 UDP 기반의 프로토콜을 설계해서 적용하면 어떨까?

사실 HTTP 프로토콜도 사양서를 읽어보면 알겠지만, 프로토콜을 직접 다루기는 그렇게 쉽지 않다. 그냥 그렇다 치고 넘어가자...



#.
이쯤에서 먼저 필자가 만든 라이브러리의 프로젝트 이름을 알려두자면(사실 만든 지 이미 꽤 됐고, 이 글은 이 라이브러리를 외부에 알리기 위한 글이다!), Fountain 프로젝트라는 이름을 갖고 있다. 프로토콜은 Fountain 프로토콜이라고 부르고 있다. 이와 관련해서 이전에 Fountain Project와 Mug를 공개합니다라는 글을 쓴 적도 있으니, 필요하다면 참고하도록 하자.

어쨌든, 프로토콜로 다시 돌아와보자. 위에서는 HTTP를 언급하였는데, HTTP는 프로토콜 자체의 명세를 제외하고 통신에 필요한 규약만 놓고 본다면 다음과 같은 특징을 갖고 있다:

  • TCP 프로토콜이다.
  • 80 포트를 통해 HTTP 요청을 처리한다.
  • 텍스트에 기반을 두고 있다.

 
마찬가지 방식으로, Fountain 프로토콜은 다음과 같은 특징을 갖고 있다:

  • UDP 프로토콜이다. (UDP에 기반하므로 요청/응답이라는 구조는 별도로 존재하지 않는다)
  • 7904 포트를 통해 Fountain 메시지를 처리한다. 
  • JSON 포맷의 텍스트에 기반을 두고 있다.

Fountain 메시지의 예는 다음과 같다 (이전에 JSON을 다뤄본 적이 있다면 익숙할 것이다) :  
{"FM":{"TEST":{"PacketIndex":1,"Base64Data":"","Checksum":237}}}
  • "FM"은 Fountain 메시지임을 식별하기 위해 사용한다.
  • "TEST"는 Fountain 메시지를 사용하는 서비스의 식별자이다. 예를 들어, 필자가 만든 Mug 서비스의 경우에는 식별자로 "MLST"를 사용하고 있으며, BITNA 앱은 식별자로 "aBIT"을 사용하고 있다.
  • 서비스 식별자 내부에는 개발자가 원하는 대로 JSON 포맷에 맞게 메시지 내용을 정의하여 사용하면 된다.

실제 프로그램을 구현하는데 있어서는 몇 가지 유의할 점이 더 있긴 하지만, 기본적인 구현에 필요한 사항은 위와 같다. 이런 방식으로 프로그램마다 다른 종류의 프로그램/서비스와 식별자가 중복되지 않도록 각자의 Fountain 메시지를 설계하여 사용하면, 얼마든지 7904 포트를 열어 필요한 메시지를 수신하여 처리할 수 있다.



#.
이 Fountain 라이브러리(일단은 이렇게 불러야 할 것 같다)는 현재 Objective-c, Java, C# 등으로 구현되어 있다. 딱히 이들 언어가 좋아서라기보다는 아이폰, 안드로이드, 윈도우 폰용 BITNA 버전을 만들다 보니 각 플랫폼 별로 구현이 된 것인데, 아직 다른 오픈 소스와 얽혀 있는 부분도 있고 해서 당장 오픈할 수는 없을 것 같다. 앞으로 어느 정도 정리되고 나면 BitBucket이나 GitHub를 통해 오픈 소스로 공개할 예정이다.

혹시 그 이전에라도 관심 있는 개발자가 있다면, 연락 주시길. 성심 성의껏 응대하도록 하겠다 : )