IT/Doc

MSN 프로토콜 분석

싸후이 2007. 2. 26. 14:23
지난 글에서 임베디드 프로그래밍의 개요와 소스 관리 그리고 임베디드 메신저의 대략적인 설계를 살펴보았다. 이번 글에서는 MSN 메신저 제작의 심도깊은 부분에 들어가서 네트워크 처리와 함께 메신저의 기본이 되는 프로토콜을 면밀히 검토하고 프로그램의 세부 설계를 해 보자.

MSN 메신저는 비공개 프로토콜이다. 처음 1.0 버전의 자료가 간단한 문서로 인터넷에 유포된 것을 수많은 개발자들이 분석하여 지금에 이르렀다. 비공개 프로토콜을 분석하는 일은 간단한 일이 아니다. 패킷을 하나하나 확인해서 그 기능이 어떤 역할을 하는지를 추정해야 하기 때문에 매우 어렵다. 하지만 다행히 MSN 프로토콜은 분석되어 있는 내용이 많이 공개되어 처음 개발을 시작하는 프로그래머에게 큰 도움을 준다.

최근 MSN 프로토콜 버전 (MSNP) 8과 9 그리고 10까지 상당 부분 분석되어 있어 큰 도움이 되고 있다. 특히 이모티콘, 파일 전송과 같이 자주 사용되는 기능의 프로토콜까지 모두 분석되어 있어 메신저 자체 기능만 제작할 사용자들은 큰 어려움 없이 제작에만 몰두할 수 있다. 그러나 분석되어 있는 부분이 많다고 해서 결코 구현하는 것이 쉬운 것은 아니다. 라이브러리나 정형화된 설계가 많지 않기 때문에 구현에 있어서 여전히 어려움이 있다.

이렇게 풍부한 분석 자료가 있으나 정형화된 설계가 없을 때에는 자신만의 ‘설계’가 꼭 필요하다. UML, 블럭 다이어그램, 흐름도, 클래스 계층도 등 어떤 형태로든 프로토콜의 스펙을 참조하여 설계를 먼저 해야 한다. 스펙을 수 차례 정독한 이후 머릿속으로 프로그램의 흐름을 파악하고 전체를 먼저 그린 다음 하나씩 세부적으로 그려가며 구체화시킨다면 성공적인 설계가 완성될 것이다.

필자의 경우 MSN 프로토콜 구현시 스펙을 한 달간 분석했다. 수많은 프로그래머들이 분석해 놓은 스펙과 만들어 둔 소스 코드를 여러 차례 분석하면서 메신저의 실체를 구체화시켜 갔다. 물론 첫 설계가 100% 성공적이지는 않았다. 그 후 2차, 3차 개발이 필요할 정도로 초기 설계는 미미했지만, 중요 골격은 지금까지도 그대로 유지될만큼 중요한 지침서가 되었다. 이번 글에서는 MSN 프로토콜의 서버 접속 부분의 스펙을 면밀히 살펴보고 설계를 한 다음 구현하는 순서로 나아가 보자.

메신저 접속 단계
이를 보면 MSN 메신저는 여러 개의 서버로 구성되어 있다. 야후, ICQ, AOL 등의 프로토콜과 비교하면 상당히 복잡하게 구성되어 있는 편이다. 게다가 접속할 때 3개의 서버를 거쳐가기 때문에 다른 프로토콜보다 접속 속도가 비교적 느리다. 게다가 서버와 메시지를 계속 주고 받으면서 접속하는 형태로 구성되어 있어서 인증 과정 중 한번의 비정상적인 메시지를 전달하게 되면 접속이 끊어진다. 메신저를 완전히 구현한 이후에는 약간 느린 동작을 하는 정도지만 구현해야 하는 입장에서 볼 때 실수를 용납하지 않는 것이 더욱 어려운 디버깅 작업을 거쳐야 한다는 것을 뜻한다. 지금부터 MSN 메신저의 서버를 하나씩 살펴보자.

◆ Dispatch Server(DS) : 최초 접속 서버(messenger.hotmail.com:1863), NS 서버의 정보를 제공
◆ Notification Server(NS) : 사용자의 접속, 채팅, 상태 등을 주관하는 서버
◆ Switch-board Server(SS) : 사용자간의 채팅을 위한 게이트웨이. 모든 채팅 창은 각각의 Switch-board Server에서 세션을 유지하고 있다.
◆ Passport Server(SSL) : 마이크로소프트(이하 MS) 패스포트의 인증 티켓을 받기 위한 서버(MSNP8 이상에서 사용)

최초에 MSN 메신저는 DS를 통해서 접속하게 된다. 이 서버로 접속하게 되면 사용자에게 ID와 프로토콜 버전 같은 간단한 정보를 보낸다. 그러면 서버로부터 NS의 주소를 보내준다. 이 NS 주소는 사용자가 접속을 끊을 때까지 계속 유지하게 되는데, 이 서버를 통해서 다른 사용자의 접속 상태나 채팅 등을 할 수 있다. 메인 서버 개념인 NS에서는 프로토콜 버전에 따라 인증 과정을 크게 두 가지로 나누어 처리한다. MSN 프로토콜 버전(MSNP) 7 이하와 8 이상으로 나누어 처리하는데 MSNP7 이하는 MD 5 암호화 모듈을 이용하여 사용자 비밀번호를 전송하고, MSNP8 이상에서는 패스포트 서버ㄴ에 SSL로 접속하여 인증 티켓을 받아오는 방식으로 인증을 처리하게 된다.

인증의 단계가 끝나게 되면 NS로부터 그룹 리스트, 친구 리스트, 친구들의 현재 상태까지 여러 가지 정보를 받아오고, 모든 데이터의 처리를 마친 이후 사용자의 상태를 온라인으로 변경시키는 명령을 전송하면 비로소 다른 사용자들의 메신저에 접속했다는 메시지가 뜬다.

MSN 프로토콜의 기본 구조
MSN 프로토콜의 기반 구조부터 하나씩 살펴보자. MSN 프로토콜은 쉽게 읽을 수 있는 ASCII 기반의 프로토콜이다. 야후나 ICQ 같은 경우 바이너리 형식을 취하고 있어서 바로 판독하기는 어렵다. 하지만 MSN 프로토콜의 경우 명령어 자체가 매우 직관적으로 분석할 수 있도록 되어 있어 프로토콜의 규약을 모르는 사람도 접근이 비교적 쉬운 편이다. 명령어를 살펴보기 이전에 MSN 프로토콜의 기본 구조를 살펴보자.

[1] Command : 명령어. 반드시 영문자 대문자 혹은 숫자 3자를 이용한다. 명령에 따라 뒤에 오는 파라미터 값이 다르다.
[2] TrID : 트랜잭션 ID, 0~232-1까지의 명령어 순번으로 모든 명령에 따라온다. 매 명령시 1씩 증가한다.
[3] Parameter : 명령어에 따른 파라미터가 오며 파라미터가 여러 개인 경우 스페이스로 구분한다.
[4] Sp(공백) : 각 부분별 구분 단위, 명령어와 TrID 사이, TrID과 파라미터 사이, 파라미터들 사이에 들어오게 된다.
[5] ‘\r\n' : 명령의 마지막을 표시한다. 일부 명령에서는 ’\r\n\r\n’으로 2번 연속해서 쓰는 경우도 있다.Command Sp TrID Sp parameter \r \n
3 … … 1 1 1 1

<그림 1>에서 보듯이 간단한 형태로 되어 있는 MSN 프로토콜은 각 명령어 자체는 매우 쉽게 처리할 수 있다. 각 명령별로 들어오는 파라미터만 처리해서 넘겨주면 어렵지 않게 각 명령을 처리할 수 있다.

<그림 1> MSN 프로토콜의 기본 구조

하지만 수많은 명령들을 공백 단위로 잘라내는 것은 꽤 귀찮은 작업이다. 게다가 명령이 길어질수록 그 작업은 더욱 힘들어지게 된다. 이를 위해 공백 단위로 데이터를 잘라내는 함수를 제작하자. 그리고 그 잘라낸 데이터별로 처리하면 조금 쉽게 사용할 수 있다. 그 부분은 뒷 부분에서 소스를 직접 보면서 살펴보기로 하고 여기서는 프로토콜의 기본 구조를 하나씩 살펴보자.

먼저 Command에는 명령어들이 들어오게 된다. 반드시 영문자 대문자 혹은 숫자 3자로 구성되며 직관적인 명령이 많다. 예를 들어 버전 정보를 의미하는 VER, 사용자 정보 전달을 의미하는 USR 등 여러 명령이 비교적 직관적으로 만들어져 있어 사용하기 편리하다. TrID는 명령어 다음에 오는 명령 순번으로 매 명령마다 1씩 증가하게 되는데 큰 의미는 없다. 필자는 처음 테스트할 때 모두 ‘0’으로 설정하고 테스트해 보았는데 아무런 이상없이 동작했다. 일단은 규약에 따르기 위해 매 명령마다 1씩 증가시켜 주자. 파라미터는 명령어에 따라 오는 값이 모두 다르다. 명령에 따라서는 파라미터가 없는 경우도 있다. 파라미터의 예를 하나 살펴보자.

VER 1 MSNP7 CVR0\r\n

이는 프로토콜 버전을 정의하는 명령이다. ‘나의 메신저 프로토콜은 MSNP7입니다. MSNP7으로 통신을 하겠습니다’라는 의미이다. 여기서 VER은 앞에서 이야기했듯이 명령어이고, 1은 TrID이다. ‘MSNP7 CVR0’는 파라미터에 해당하고 버전 정보에 딸려오는 것이다. ‘공백’은 각 부분을 구분하는 데 사용되며 ‘\r\n’은 명령의 마지막을 나타내는 데 사용한다. ‘\r\n’을 생략하거나 ‘\n’만 쓰게 되면 정확한 명령 전달이 안되므로 정확하게 사용하여야 한다.

MSN 프로토콜 명령어
약 40여개 이상의 명령으로 구성된 MSN 프로토콜의 명령어들은 그 기능이 버전 정보 전송에서부터 대화 요청까지 다양하다. 각 명령은 대부분 독립적으로 동작하는 경우가 많아서 객체 단위나 함수 단위로 처리하면 쉽게 처리할 수 있다. 특히 명령어 추가나 삭제가 쉬운 편이라서 추후 확장이 매우 용이하다. 자 처음 MSN 서버에 접속했다는 메시지가 뜨는 것을 보기 위한 명령어를 <표 1>에서 살펴보자.

명령어
내용
VER
서버의 사용자 사이의 프로토콜 결정
ILN
친구 목록 중 온라인 상태에 있는 친구들의 상태 표시
LSG
사용자 리스트 정보
QRY
서버로부터 오는 응답
CHL
첼린지(Challenge) 인증 메시지
H
사용자가 서버로 보내는 PING 명령
QNG
PING에 대한 응답
SYN
서버와 클라이언트언트의 정보 동기화 명령
GTC
프라이버시 셋팅(채팅 요청 등)
BLP
프라이버시 셋팅(차단 또는 수락 리스트 등)
BPR
사용자 전화번호
CVR
사용자와 서버 작동 시스템에 대한 버전 정보를 전달
INI
서버에 인증 프로토콜을 요청
USR
서버로 사용자 인증
XFR
새로운 세션 생성
CHG
사용자 상태 변경
FLN
비동기 사용자 상태 정보 전송(오프라인)
NLN
비동기 사용자 상태 정보 전송(온라인,상태 정보)
OUT
로그아웃
<표1> MSN 프로토콜의 명령어

<표 1>을 보면 상당히 많은 명령들이 접속에 사용되는 것을 볼 수 있다. 체팅용 명령어를 제외한 대부분을 나열한 것인데 접속시 이 명령을 이용해 하나 하나 절차를 밟아간다면 정상적인 로그인을 할 수 있다. 명령은 크게 2가지로 나누어지는데 서버로 보내는 명령과 서버에서 오는 명령이다. 서버로 보내는 명령은 주로 정보 요청을 할 때 사용하고, 서버에서 오는 명령은 그 요청에 대한 응답이다. 예를 살펴보자.

VER 1 MSNP7 CVR0

이와 같이 명령을 서버로 보내면 서버는 다음과 같이 응답을 한다.

VER 1 MSNP7 CVR0

똑같은 명령이 되돌아 온 것 같아 보이지만 의미는 서로 다르다. 보낼 때의 의미는 ‘MSNP7으로 통신을 하겠습니다’이지만 돌아온 메시지의 의미는 ‘MSNP7으로 통신을 허락합니다’라는 의미이다. 앞에서 보는 명령은 ‘VER’로 동일하지만 서로 다른 방식으로 처리해야 한다. 즉, 명령어 처리를 2가지로 나누어서 하여야 하는 것이다. 보내는 부분과 받는 부분으로 나누어 처리하고 명령어가 섞이지 않도록 각별히 주의하여야 한다.

암호 인증하기
MSN 메신저는 지난해 10월 15일 이후 SSL이라는 장벽을 설치해 놓았다. 이 장벽 때문에 몇몇 MSN 클론들은 추가적인 개발이 이루어지지 않고 있다. 하지만 대부분의 MSN 클론들은 SSL을 각자의 방법으로 뚫고 들어갔다. 필자의 팀 역시 지난해 10월 15일 이전에 SSL 장벽을 뚫었다. 하지만 결코 쉽지는 않았다. 가장 힘들었던 점은 정보의 부재였던 것 같다. MSN 메신저에서 MS 패스포트 서버의 SSL 장벽을 뚫지 못해 암호 인증을 못한다면 아무런 의미가 없을 것이다.

그 당시 일부 개발자 그룹에서 MSNP8을 통하여 패스포트 서버에서 인증받는 방법을 공개하기 시작했다. 필자 팀 역시 MSNP7까지 구현된 상태였기 때문에 일부분을 수정하면서 테스트하는 것은 그리 어렵지 않았다. 외국 사이트를 번역하며 SSL 관련 정보를 찾았고, 그 정보와 SSL을 통해 패스포트 서버로부터 인증을 받았다.

하지만 또 하나의 난관이 있었다. SSL을 어떻게 임베디드 시스템에 탑재할 것인가 하는 문제였다. SSL은 꽤 덩치가 큰 라이브러리이고 포팅을 하게 된다면 시간이 적지 않게 걸리는 라이브러리였다. 개발한다는 것은 단기간 내에는 엄두도 낼 수가 없었다. 수많은 사이트를 돌아다녀 보았지만 하나같이 모두 SSL은 라이브러리를 이용할 것을 추천했다.

필자는 OpenSSL을 이용해 ARM 버전으로 컴파일하기로 결심했다. OpenSSL은 임베디드용으로 이용하기에는 덩치가 크지만 100% 소스가 공개되어 있고 다양한 플랫폼을 지원하기 때문에 ARM 버전으로 컴파일하기가 상대적으로 편할 것 같다는 생각을 가지고 작업을 했다. OpenSSL을 바로 ARM 버전으로 컴파일했을 때 동작하지 않았다. 수차례 반복과 실패 끝에 결국 그 방법을 찾아냈다. 다음의 순서대로 따라한다면 어렵지 않게 ARM용으로 컴파일할 수 있을 것이다.

[1] www.openssl.org에서 소스를 다운받아 압축을 해제한다.
[2] ./Configure linux-elf-arm --prefix=/usr/local/SSL --openssldir=/usr/local/SSL/OPENSSL no-threads no-zlib no-asm
[3] Makefile에서 CC를 크로스 컴파일러(ex. arm-linux-gcc)로 변경한다. Makefile이 여러 개인데 모두 변경해 주어야 한다.
[4] apps 디렉토리 안에 있는 makefile에서 LIBSSL에 ‘-ldl’을 추가시킨다. 또 test 디렉토리의 makefile에 LIBCRYPTO에 ‘-ldl'을 추가시킨다.
[5] make; make install을 통해서 컴파일한다.
[6] arm-linux-gcc -print-search-dirs로 라이브러리 검색 디렉토리 목록을 찾은 후 액세스 가능한 디렉토리에 생성된 라이브러리 파일 2개와 pkgconfig 디렉토리를 복사한다.

이와 같은 셋팅을 하고 난 이후에는 컴파일러를 크로스 컴파일러로 선택하고 컴파일 옵션에서 ‘-lssl -lcrypto’를 추가한 이후에 SSL의 헤드 파일을 인클루드시키면 바로 사용할 수 있게 된다. SSL의 경우 매우 덩치가 큰 라이브러리이다. 임베디드 장비에서는 사용하게 되면 프로세서 속도는 그리 느려지는 편은 아니지만 메모리 문제는 한번쯤 만나게 될 것이다. 이때는 동적 컴파일과 정적 컴파일을 선택하여 자신의 환경에 맞는 설정을 하면 그 문제는 해결할 수 있다.

필자의 팀 역시 SSL을 올리고 나서 임베디드 보드에서 자원 부족 현상으로 프로세서가 죽는 현상이 발생했다. 여러 가지 방법으로 해결해 보려고 애쓴 덕분에 결국 해결은 했지만 SSL이라는 라이브러리의 덩치를 무시할 수는 없었다. 약 메모리를 5MB 정도 추가로 소비하기 때문에 SSL을 올릴 예정인 임베디드 프로그래머는 메모리 할당에 각별히 신경써야 한다.

친구 리스트 처리하기
암호 인증이 끝난 이후 처리해야 할 또 다른 관문이 있다. 친구 리스트 처리하기가 바로 그것이다. 수많은 데이터가 연속해서 들어오기 때문에 더 어렵고 하나 하나 연결 리스트로 만들어서 추가시켜야 하기 때문에 더 힘들다. 게다가 버전에 따라 조금씩 바뀌기 때문에 버전별 처리를 별도로 해야 한다. 기본적인 맥락을 보고 하나씩 자세히 살펴보자.

SYN TrID 0

앞과 같이 TrID에 해당하는 명령어 순번을 넣어주고 NS로 메시지를 보내게 되면 NS에서 상당량의 데이터를 보내올 것이다. 그 정보 중 대표적인 것에는 그룹 리스트, 친구 리스트가 있는데 그룹 리스트는 해당 그룹 이름과 그룹의 고유 번호를 저장하고 있으면 된다. 친구 리스트는 다음 네 가지의 리스트로 나뉜다.

[1] Forward List(FL) : 가장 기본이 되는 리스트. 사용자의 전화번호와 닉네임 등의 정보가 나온다. 최대 150명까지 입력할 수 있다(2003년 03월 23일 이후).
[2] Reverse List(RL) : 상대방이 승낙했을 때 처리되는 리스트. 임의로 추가 또는 삭제가 불가능하다. 임의 추가 삭제를 시도할 경우 에러 메시지없이 즉시 접속이 끊어진다.
[3] Allow List(AL) : 대화 수락 리스트
[4] Block List(BL) : 대화 차단 리스트

4개의 리스트 데이터를 모두 받아온 다음, 분류에 따라 처리를 해야 한다. BL에 포함된 친구에게는 사용자의 접속 상태를 비롯하여 각종 메시지가 전달되지 않는다. 물론 대화를 시도하려고 해도 오프라인으로 표시되어 대화를 할 수 없다. 당연한 이야기겠지만 AL과 BL에 동시에 동일한 ID가 포함될 수는 없다. 만약 동일한 ID를 입력하게 된다면 다음과 같은 오류를 발생시킨다.

[송신] ADD 15 AL example@passport.com example@passport.com\r\n
[수신] ADD 15 AL 108 example@passport.com Mike\r\n
[송신] ADD 16 BL example@passport.com example@passport.com\r\n
[수신] 219 16\r\n

수신의 명령어 라인에 ‘219’라는 에러 번호를 수신하게 되는 것이다. 이처럼 친구 리스트를 관리할 때는 각별한 주의를 필요로 한다. 네 개의 리스트가 서로 얽히지 않게 분류하는 것이 가장 중요하다. 필자의 경우 C 언어의 싱글 링크드 리스트를 이용하여 구성했다. 친구가 많은 경우 이 데이터의 양은 방대해진다. 임베디드 시스템에서의 프로그래밍이라면 이런 부분에 좀 더 민감하게 처리할 필요가 있다. 필자는 다음과 같이 4개의 친구 리스트 구조를 약간 변경하여 하나의 리스트처럼 처리하는 방식을 이용했다.

int        sock;
char*        id;
char*        name;
int        status;
int        group_eid;
int        forward;
int         reverse;
int        allow;
int        block;

MSN_LIST*next;

4개의 리스트를 모두 만든다면 ID 값과 별명 등의 정보는 중복된다. 특히 이들을 저장하기 위한 구조체는 더욱 복잡해진다. 4개의 리스트를 하나로 줄이면 친구 리스트를 저장하기 위한 메모리를 최소 50% 이상 절약할 수 있다. 앞과 같은 구조로 제작하여 FL, RL, AL, BL 중 하나라도 데이터가 있다면 해당 구조체가 생성되고 해당하는 변수를 ‘1’로 설정하면 된다. 조금 더 메모리를 절약하기 위해서는 비트(bit) 단위의 처리를 해도 무방하나 가독성을 위해 정수형으로 처리하였다. 각 구조체는 ‘싱글 링크드 리스트’로 연결하여 정보를 저장하게 되면 친구 리스트의 구성은 완료된다.

URL Encoding/Decoding
인터넷을 검색하다 보면 흔히 다음과 같은 주소를 발견할 수 있다.

http://wwl68.daum.net/Mail-bin/hm_frame.cgi?url=/Mail-bin/mail_list?dummy=232323643&FOLDER=%B9%DE%C0%BA%C6%ED%C1%F6%BA%B8%B0%FC%C7%D4&_top_hm=w_up_inbox

다른 것들은 직접적으로 읽을 수 있는 부분인데 ‘%B9%DE...’ 부분은 읽을 수 없다. 이는 바이너리 데이터를 직접 표시하지 않고 퍼센트 기호와 함께 헥사 코드(Hexadecimal code)를 표시한 것이다. 이 코드 세 자리를 한 자리의 바이너리 코드로 변환하면 간단하게 처리할 수 있다. MSN에서도 이러한 URL-Encoding/Decoding이 필요한데, 인증 과정에서부터 필요한 것이기 때문에 사전에 모듈화시켜 둘 필요가 있다.


<리스트 1>은 최초 접속하여 초기 인증을 완료하는 과정까지 보여주고 있다. 인증 과정 중 2번 URL-Decoding이 필요한데 패스포트 서버 접속 직전과 인증과정의 마지막에서 필요하다. 마지막의 명령을 보면 ‘example%20display%20name’이라 되어 있는데 헥사 코드 20은 ‘ ’(공백)을 의미한다. 즉, example display name과 동일한 의미이다.

다만 MSN 프로토콜 자체에서 공백을 파라미터 구분 단위로 인식하므로 이를 방지하기 위한 수단일 수도 있다. 필자는 이 부분의 구현시 % 기호를 인식하는 필터 함수를 만들었다. 이는 매우 유용하게 이용할 수 있는데 입력받은 데이터 중에서 % 기호가 있다면 그 다음 두 바이트의 값을 계산하여 하나의 바이너리로 만드는 함수를 필요한 곳에 넣기만 하면 된다.


문자열을 토큰화하기
<리스트 1>을 살펴보면 알 수 있듯이 MSN 프로토콜은 공백을 단위로 구분한다. 게다가 한 명령줄에 다수의 파라미타가 오는 경우 모두 잘라서 보관하고 있어야 한다. 이를 위해서 간단한 함수를 만들어서 이용하면 편리하다. <리스트 3>을 보면 2개의 함수가 있는데 하나는 공백을 단위로 토큰화시키는 함수이고 다른 하나는 할당된 메모리를 해제하는 함수이다.

임베디드 프로그래밍에서는 효율성이 매우 중요하다. 특히 메모리 효율이 더욱 중요한데 한번 사용한 메모리를 돌려주는 것을 절대 잊어서는 안된다. 그리고 상당 수의 버그들은 메모리 관리를 철저하게 하지 않아서 생기는 것들이다. 메모리 관리를 위해서는 함수 내에서 메모리를 할당하는 것을 피해야 하지만 편의성이 매우 뛰어나기 때문에 함수 내에서 메모리를 할당하고 추후 별도로 해제시켜 주는 함수를 제작해 이용했다.

필자가 MSN 메신저를 제작하면서 디버그 기간을 최소화할 수 있었던 가장 큰 요인은 바로 이 메모리 해제에 있다. 완벽할 수는 없지만 한번 사용한 메모리 중 필요가 없는 부분은 그 즉시 해제시킴으로써 프로그래밍 중간에 발생할 수 있는 다양한 버그를 미연에 방지했다. 특히 최종 디버깅 작업을 할 때 메모리 할당 함수와 해제 함수가 짝을 이루어 사용함으로써 메모리 누수 등의 버그를 상당히 많이 해결할 수 있었다.


MSN 메신저 세부 설계하기
앞에서 MSN 프로토콜과 그 프로토콜을 처리하기 위한 몇 가지 함수들을 살펴보았다. 이제 총알은 준비된 셈이다. 총에 총알을 넣고 조준하여 쏘기만 하면 된다. 머릿 속으로 지금까지의 내용을 정리해 보자. 처음 DS로 접속을 하여 NS로 접속하고 MS 패스포트 서버를 통해 인증 티켓을 받은 후 NS로 그 티켓을 넘긴다. 그 이후부터는 NS와 데이터를 주고받게 된다.

그룹 리스트, 친구 리스트 그리고 약간의 정보를 받고 처리한 다음 온라인 상태로 설정하면 접속이 완료된다. 머릿 속에서 <그림 2>와 같은 그림이 그려졌을 것이다. <그림 2>와 같은 그림을 떠올릴 수 있다면 이미 MSN 프로토콜 접속부를 완성한 것과 다름이 없다. 순차적으로 메시지를 주고 받으면서 이어가는 지루한 작업은 단지 시간과의 싸움일 뿐이다. 좀 더 세부적인 그림을 그려보자.

<그림 2> MSN 접속 부분의 간략한 흐름도

<그림 3>은 인증에서 접속 완료까지를 좀 더 세부적으로 나타내었다. 응답 루틴 아래에 명령어들은 해당 명령을 서버로부터 수신했을 때 필요한 처리 과정을 넣으면 될 것이다. 그리고 다음 과정을 진행하기 위한 명령을 송신할 것이다.

<그림 3> MSN 접속 부분의 세부적인 흐름도

간단한 예를 하나 살펴보자.

[송신] VER 1 MSNP8 CVR0\r\n
[수신] VER 1 MSNP8 CVR0\r\n
[송신] CVR 2 0x0409 win 4.10 i386 MSNMSGR 5.0.0544 MSMSGS

앞의 예는 <리스트 1>의 첫 부분이다. 최초 VER을 송신한 이후 다시 VER을 수신할 때에는 파라미터에 따라 일련의 처리 과정을 거쳐 처리하면 될 것이다. 그리고 다음 명령인 CVR을 전송함으로써 자연스럽게 다음으로 이어 갈 수 있다. 여기서 주의해야 할 것은 같은 명령이라 하더라도 파라미터의 값에 따라 서로 다른 처리를 해야 하는 경우가 있다는 것이다. 예를 들면 LST 명령은 파라미터에 따라 FL, RL, AL, BL로 나뉘기 때문에 각각의 처리를 별도로 해 주어야 한다. 다른 명령들 역시 각각의 기능에 맞게 처리할 수 있도록 제작하면 될 것이다.

MSN 프로토콜의 구현
이제는 프로그래밍의 준비가 다 됐다. 기본 지식과 세부 기술들 그리고 설계까지 마친 상태이므로 코딩 자체는 그리 어려운 작업이 아니다. 다만 지루한 시간과의 싸움이 될 수는 있다. 각 명령어별로 스펙과 처리 방법을 전부 익혀야 한다. 일부 명령은 다양한 파라미터를 가지고 있어서 그것 별로 처리하는 루틴이 필요하기도 하다. 하지만 이미 프로토콜에 대한 기본기를 닦았기 때문에 어렵지 않게 해결할 수 있을 것이다.

짧은 지면상에서 많은 예제 소스를 설명하기는 어렵다. 하지만 가장 핵심이 되는 부분의 소스인 ‘NS 메시지 응답 루틴’과 ‘CHG 명령 전송’의 예를 살펴보면 다른 명령들은 쉽게 제작할 수 있을 것이다.


<리스트 5>의 예제는 msn_recvln()를 통해서 명령어 한 줄을 받아오고 msn_str_explode()를 이용하여 공백 단위로 데이터를 토큰화시키는 것이다. 그리고 각 명령별로 필요한 처리를 해 주면 된다. 각 명령어가 독립적으로 존재할 수 있으므로 명령어의 추가나 삭제가 쉬운 편이다. 필요한 기본 소켓 명령들을 MSN 메신저에 맞게 최적화 시켜 전역 함수로 선언해 두면 사용하기가 더욱 편리해진다.


<리스트 4>는 CHG라는 명령을 보내기 위한 함수인데 파라미터로 받는 int status에 따라서 보낼 상태 값을 결정하게 된다. 하나의 임시 버퍼에 문자열을 조합하는 작업이다. 마지막 함수인 msn_sendln()은 send() 함수와 동일한 기능을 하는데 좀 더 간략화시킨 것 뿐이다. 포인터 변수인 account는 사용자 계정의 포인터를 뜻하며 모든 정보의 시작이라 해도 과언이 아닐 정도로 다양한 정보가 들어있다. 소켓 디스크립터 역시 MSN_ACCOUNT 안에 들어 있다.

다음 글에는 친구와 대화하기
이번 글에서는 DS에서 NS까지 살펴봤다. 어렵고 막막하게만 느껴지던 MSN 프로토콜이 그렇게 까다로운 프로토콜이 아니라는 점을 알게 되었을 것이다. 단지 문자열의 조합으로 이뤄지는 작업이 반복될 뿐이다. MSN 명령어를 처리하는 한 개의 함수를 제작한 이후에는 두 개, 세 개로 확장해 가기가 매우 쉽다. 비슷한 명령으로 처리되는 내용도 상당히 많고 대다수의 MSN 명령을 처리하는 함수들은 30줄을 넘기지 못할 정도로 짧고 간결하게 처리할 수 있다.

짧은 코딩이라고 해서 결코 좋은 것은 아니지만 그만큼 간결하게 처리할 수 있다는 것을 의미한다. 필자가 구현한 MSN 프로토콜은 내부적으로 3개의 레이어로 구현되어 있다. 하부는 소켓 통신을 하는 부분, 중간 부분은 MSN 프로토콜을 처리하는 부분 그리고 상부는 추상 계층과 연결하기 위해 중간 부분을 관리하는 부분으로 되어있다. 이런 계층 구조는 중복되는 코딩을 많이 막아주기 때문에 개발자에게 코딩의 노동(?)을 줄일 수 있다.

다음 글에서는 SS를 이용하여 친구와 대화하기를 시도할 것이다. 이번 글을 통해 프로토콜의 기본 구조를 알았기 때문에 다음 글은 그 응용이 될 것이다. UTF-8을 이용한 한글 전송을 비롯하여 채팅 서버(SS)의 세션 관리 등을 소개할 것이다.

MSN 프로토콜을 제작하려고 한다면 가장 먼저 할 일은 스펙을 철저히 분석하는 작업이다. 그리고 설계를 한 후 코딩에 들어가기를 강력하게 권한다. 이미 잘 짜여진 공개 소스들을 살펴보며 자신의 MSN을 설계해 보자. 그리고나서 코딩을 해도 늦지 않다. 설계를 완성했다면 이미 반은 제작한 것이나 다름없다. 지금이라도 조급해 하지 말고 종이 한 장을 꺼내 설계를 시작해 보자. 그러면 어느 순간 자신만의 MSN이 완성되어 있을 것이다. @

'IT > Doc' 카테고리의 다른 글

Email Disclaimer  (0) 2007.04.24
linux wireless networking guide  (0) 2007.04.05
ADSL Linux Howto (ADSL Linux 접속)  (0) 2007.02.28
Wi-Fi 참고  (0) 2007.02.15
List of Unix programs  (0) 2007.01.25