원문 : http://www.gamedevforever.com/39

안녕하세요? 요양과 잉여짓으로 바쁜 Rhea Strike 입니다.
정작 당분간 하지 않을려했던
잉여 프로그램들은 짜고 있지만 가장 맘먹은 동인지 원고와 짤 제작, 여친 만들기 등이 늦어져 무척 가슴 아픈 나날입니다.

다들 쟁쟁한 주제들과 현업에서 연구하신 소재들로 막강한 화력을 자랑하시지만
실력이 딸려 제가 준비한 것은 네트워크 게임을 만드는 튜터리얼 연재입니다.
이 연재를 시작하기전 무슨 주제로 어떻게 이어갈까 고민을 잠시 했는데요,
잠시 이 링크 http://rhea.pe.kr/493 를 읽어주시고 연재글을 봐주시면 감사하겠습니다.

연재의 초기는 기본적인 채팅으로 시작될 것입니다.
이는 네트워크 프로그래밍에 익숙하지 않는 분들과 있을지도 모를 학생들을 위한 것이기도 합니다만,
매 연재 속에 점점 단순 채팅 프로그램이 게임과 아키텍쳐로 점차 커져가며
퇴근길 지하철에서 전체 게임을 위해서 한번 곱씹어 생각해볼만한 꺼리를 던져드리는게 제 목적입니다.

자, 그럼 지루한 서론을 마치고 우선은 모두들 알고 계신 내용이라도 복습의 의미로
기초부터 시작해보지요. ...역시 지루하군요.

짤에 별다른 의미는 없습니다만 이런 여친을 찾고 있습니다. 도움을 바랍니다.

짤에 별다른 의미는 없습니다만 이런 여친을 찾고 있습니다. 도움을 바랍니다.


1. 윈속의 역사
이미 게임을 하나 운영하고 있는 곳이라면 많은 것이 갖춰져 있습니다.
개발자 이외에도 DB, 서버, 패치 시스템, 그리고 그것을 돌려주는 여러 DBA, SE분들이 있습니다.
그러나 아무 것도 없는 곳에서 새로 온라인/네트워크 게임을 만든다면 맨땅에 헤딩을 해야 합니다.

우선 기획서가 나오고 DB라던가 여러 가지 인프라들이 먼저 있어야할 것이지만,
가장 단순하게 윈도우 서버와 클라이언트부터 만든다고 가정합시다.
자, 그럼 서버는 어떻게 만들어야 할까요?
서버를 만들기 전에 서버와 클라이언트에는 어떤 "Winsock Model"을 사용할런지부터 결정해야 합니다.

원래 소켓은 Windows 운영체제가 아니라 BSD UNIX에서 먼저 만들어졌습니다.
그래서 초창기 윈도우 3.1 같은 경우에는 소켓(http://www.joinc.co.kr/modules/moniwiki/wiki.php/man/12/BSD%20socket)
이 아예 존재하지 않았습니다.

이는 단순 소켓 지원 여부를 떠나 당시 마이크로소프트(앞으로 MS)의 중요한 의사 표방이기도 합니다.
소켓은 TCP/IP, UDP를 위한 라이브러리이고 TCP/IP, UDP같은 프로토콜은 바로 인터넷의 기반이 되는 프로토콜들이죠. 모든 것을 집어삼킨 HTTP 역시 TCP/IP 80번 포트를 이용한 텍스트기반 프로토콜 아니겠습니까?

다시 말해, MS는 현재의 인터넷이 이리 사용될지 몰랐습니다, 무려 90년대 중반까지도요!
MS는 소켓과 TCP/IP가 아닌 자신들의 프로토콜로 MS-Network라는 현재의 인터넷과 비슷무리하지만 다른 개념을 만들고 싶었습니다.

그 망상은 Windows 2000 이 등장하면서 공식적으로 사라졌는데 \\로 접근하던 다른 PC접근과 네트워크 공유를 NetBIOS(NetBEUI) 설치 없이 TCP만 설치하면 되도록한 것이 그 증거(?)라고 볼수 있습니다. 정확하게 말하면 NetBIOS over TCP/IP 이란 개념인데 아뭏든 90년대에도 MS는 인터넷도, TCP도, BSD 소켓도 반대! 그냥 MS껄로 다 정ㅋ벅ㅋ할꺼야!!였었습니다.

그래서 Win 3.1에서 Netscape로 웹서핑을 하던 우리들은 Win 3.1에는 없는 TCP/IP 프로토콜을 쓰기 위해
트럼펫 윈속(http://www.trumpet.com.au/index.php/downloads.html) 을 따로 설치해 썼었었습니다.
진짜 일반 사용자가 쓸수 있는 MS표 소켓이 나오는 것은 Win95 때부터입니다.

추억 돋는 화면 하나 더! http://www.loblolly.net/~rddecker/helppages/win31trmwns.htm



....................




...........





.....


..


그러나 어쩌겠습니까?
모두가 알다시피 인터넷(다시 말해 TCP/IP과 그것을 쓰기 위한 BSD Sockets)이 대세가 되었는데요.


이 당시 빌횽의 마음...

이 당시 빌횽의 마음...









할수 없이, MS는 BSD 소켓과 똑같은 윈도우용 소켓 라이브러리를 제작합니다.
BSD 소켓 4.3에 기반해 1993년 WinNT 3.51과 함께 Winsock 1.1을 공개합니다.
즉, Winsock은 WINdows + SOCKet의 약자지요.

정말로 이때 빌횽은 전세계 개발자들에게 이말을 들었습니다.

정말로 이때 빌횽은 전세계 개발자들에게 이말을 들었습니다.




이 윈속 1.1은 놀랄만큼 BSD 소켓과 동일합니다.
고인이신 스티븐슨의 UNIX Network Programming에 있는 소스 대부분이 헤더 파일만 약간 바꾸면 그대로 돌아갈 정도입니다.

네트워크 게임계의 프로메테우스, 古 리차드 스티븐슨

네트워크 게임계의 프로메테우스, 古 리차드 스티븐슨

이는 다이렉트X 초창기의 모습과 유사합니다. DOS 게임 개발자들에게 복잡한 Windows Procedure와 콜백을 잘 몰라도 DOS 게임을 그대로 포팅할수 있도록 한 배려와 비슷한 모습입니다.

하지만 이때까지도 MS는 TCP/IP의 가능성을 잘 몰랐습니다.
...그래서 Winsock 1.1을 사용하는 Windows 서버는 ....상당히 구렸습니다.
윈도우는 서버로는 절대 안된다는 말이 이때부터 흘러나왔죠.

그러나 1995년 WinNT 4.0의 등장과 함께 선보인 Winsock 2.0부터 상황은 달라지기 시작합니다.
아키텍쳐부터 싹 달라졌습니다.
1.0과의 호환성은 100% 유지하면서 Windows에 최적화된 진짜 Winsock이 등장한 것입니다.
(달라진 아키텍쳐로 "개인방화벽" 프로그램이 만들어질 수 있었습니다. 자세한건 "SPI"로 검색을 ^^)

오오미 윈속이랑께~!!

오오미 윈속이랑께~!!

현재 우리가 주력으로 사용하는 Winsock은 16비트 모드가 제거되어 1996년에 발표된 2,2를 사용하고 있으며
Win8이 등장할때 Winsock에 Registered IO extensions 이 추가된다고 하는데 간만에 Winsock에 새로운 기능이 추가되는 것이라 기대가 큽니다.

2. 윈속 모델

모델의 좋은 사례, Winsock에게도 공식 모델이 필요합니다.

모델의 좋은 사례, Winsock에게도 공식 모델이 필요합니다.


드디어 오늘 기초의 핵심인 윈속 모델 이야기를 하겠습니다.
안타깝게도 윈속이 버전업이 되었다고 해서 윈속의 성능이 공짜로 올라가는 것은 아닙니다.


네트워크 프로그래밍에서 첫쨰 난관은 언제 "1) 누가 서버에게 Connect를 해 올줄 모른다"는 점입니다. 그리고 이때 클라이언트가 Connect를 해오는 순간, 아주 빠르게 accpet()를 해줘야 합니다. 접속이 수천개가 되면 이 작업 역시 딜레이가 걸리고 컨텍스트 스위칭이 걸립니다.
그런 다음, 일단 양쪽이 연결이 되면 "2) 서버든 클라든 언제 상대방의 메시지를 듣기 위해 recv()를 해야할지 모른다"는 점입니다(아시겠지만 연결을 끊는 close() 역시 사실은 상대방에게는 recv()입니다.)

언제 이벤트가 오는지 모르는 싯점을 체크해봤습니다.

언제 이벤트가 오는지 모르는 싯점을 체크해봤습니다.

이미지 출처 : http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzab6%2Frzab6connectionor.htm

사실 게임 클라이언트처럼 소켓 5개 정도에서는 큰 차이가 없습니다만,
서버는 한 프로세스가 수백~수천개의 접속을 관리해야 합니다.
이를 위해 단일 thread를 돌리는 방법으로는 고작 수십명이면 금방 한계에 찹니다.
아무리 서버장비가 좋아도 똑같습니다.
왜 단일 thread는 안될까요?
thread라는게 마법은 아닙니다. 단순히 CPU 한계치까지 일을 시킬 뿐, 계속 루프를 돌며 찌질하게 검사하는 역활 밖에 할 수 없습니다.

좋은 서버란 가장 적은 thread와 CPU를 사용해 가장 많은 접속을 빠르게 처리해줄수 있어야 하며 이를 위해 두가지 개념을 먼저 알아야 합니다.

우선 비동기(asynchronous )라는 개념이 들어갑니다.
쉽게 말해 계속 루프를 돌며 검사하는게 아니라 클라이언트에게 접속요청이 왔거나 메시지가 왔을때에만
해당 소켓에게 알맞은 작업을 해주는 것입니다.

그래서 비동기 Winsock을 위해 4가지 모델이 고려되었습니다.







이중 알맞은 Winsock Model 네명을 고르시오.

이중 알맞은 Winsock Model 네명을 고르시오.






...는 아니고...





모델명 특징 기타
Window Message Select
(WSAAsyncSelect)
소켓 이벤트를 윈도우 메시지를 이용해 알려준다. 윈도우 메시지를 이용하므로 반드시 윈도우가 생성되어야 하며 MFC의 CSocket 클래스가 바로 이 모델.
Kernel Event Select
(WSAEventSelect)
소켓 이벤트를 커널 이벤트로 알려준다. 한 객체당 최대 64개의 이벤트 밖에 못쓰므로 서버로 쓰기 위해서는 코드가 쓸때없이 복잡해짐.
Overlapped I/O
(Overlapped)
소켓 이벤트를 비동기 읽기/쓰기를 위한 오버랩 구조체를 이용해 알려준다. 위의 둘과 달리 Overlapped와 IOCP는 Win95, 98에서는 안됩니다.

이 기법은 파일에 비동기로 읽기쓰기를 같이 하기 위함이었으나 소켓이든 파일이든 둘다 같은 HANDLE 이잖아요? ^^

역시 MS의 꽁수의 결정체라 불릴만 합니다.
Overlapped I/O + IOCP
(IOCP)
위의 방식에 더해 커널 큐잉을 통해 소켓 이벤트를 통보받는다. 사실상 윈됴에서 서버는 이넘으로 정해져있다. C#에서 비동기 선언해도 IOCP가 만들어진다.

결론은 서버는 IOCP가 정답입니다!
...이런 모델들입니다.


두번째는 넌블로킹(non blocking)개념입니다.
단일 thread가 아닌 비동기 서버를 짰다고 합시다.
이때 어떤 이벤트가 일어나면 비동기로 OS가 서버에게 알려줍니다.
그러나 send(), recv() 같은 함수는 블로킹(blocking)함수 입니다.
send(), recv() 작업이 끝날 때까지 서버 프로그램은 다음 일을 할수 없습니다.
이때 WSASend(), WSARecv같은 넌블로킹 함수로 대체한다면, 작업이 끝나기 전에 서버는 다음 작업으로 진행될 수 있습니다.

아마 넌블러킹 함수는 네트워크 프로그래밍에서 처음 보신 분들도 있을 것이지만,
수많은 I/O관련 함수들의 고급은 전부 넌블러킹으로 흘러가게 됩니다.
넌블러킹 함수는 어쩔수 없이 Callback 함수가 반드시 뒤따라갑니다.
그래서 처음 접하면 조금 어렵습니다............................................가 아니라 실은 많이 어렵습니다,
거기에 Overlapped나 IOCP같은 비동기 통보까지 더해지면 아주 복잡해집니다.

다행히 클라이언트에서 주로 쓰이는 WSAAsyncSelect 모델과 WSAEventSelect 모델에서는 넌블로킹이 그리 중요한 개념은 아닙니다. 유선 인터넷이 하도 빨라서요...
클라이언트에서 넌블로킹을 잘못 쓰면 소켓 응답 오기 전까지 게임 프로그램 자체가 위험해질수도 있고
그 사이 화면 UI처리같은 다른 할일들도 신경을 써야 합니다(<-이거 은근 고급 토픽입니다.).

그렇다고 클라이언트=블로킹이 절대 아닙니다.
최근 스마트 디바이스에서 SNS클라이언트와 SNG와 같은 게임에서는 불안한 3G 네트워크 때문에
요즘은 클라이언트도 비동기 + 넌블로킹이 대세입니다.

3G가 워낙 느리고 자주 끊기니, 일단 클라이언트에서는 send()한 것으로 치고
유저에게 다음 작업을 할수 있도록 해주는 것입니다.
혹시 안된다면 나중에 에러 메시지가 뜨며 다시 하라고 알려줍니다.

SNG를 안하시는 분들도 트위터같은 앱에서 자주 보셨을 것입니다.
재미있게도 이런 처리 방식은 트위터 웹페이지에서도 확인할 수 있습니다.
물론 저도(사실은 밑에 팀원 갈궈서 하게 했습니다... >_<;;; ) SNS 만들때 이렇게 했고요.

만약 단순 블로킹이라면 이런 상황 자체를 볼수 없을 것입니다.

만약 단순 블로킹이라면 이런 상황 자체를 볼수 없을 것입니다.


이는 We Rule같은 게임이 네트워크가 불안정할때 쉽게 확인될수 있습니다. 단순해 보이는 SNG도 3G라는 현실로 제법 복잡한 네트워크 구현 기술을 알아야 합니다.

...이슬이...드립 치고 싶습니다.



3. 오늘의 소스
오늘의 소스는 간단한 클라이언트/서버 소스입니다.
클라이언트는 MFC로 만들었고 Winsock Model 역시 CSocket, 즉 윈도우 메시지 방식입니다.
서버는 boost::asio로 짰습니다. 기본 TCP 서버를 그대로 썼습니다.
단, 한 파일로 설명된 소스를 보기 좋게 클래스별로 나눠놨습니다.
이 정도면 네트워크 프로그래밍에 익숙하지 못한 초보분이라도 약간의 노력이면 다음 시간부터 따라오실수 있을 것입니다.

이 간단한 프로그램들로 네트워크 게임을 위한 첫 삽질이 시작될 것입니다.




4. 더 생각해볼꺼리

1) 심심하고 정말정말 할일이 없는 날, 단일 thread 방식의 채팅 서버와 윈도우 메시지 방식의 채팅 서버를 만들어 최대 몇개의 클라이언트에게 원할하게 메시지를 돌려주는지 직접 재어보세요.
2) 클라이언트가 블로킹으로 작동되어야할 경우와 넌블로킹으로 작동되어야할 경우를 생각해보세요.
3) 클라이언트가 블로킹과 넌블로킹, 모두가 동시에 필요하다면 소켓 라이브러리를 어떻게 만들어야할까요?
(힌트 : MFC에서 CSocket 와 부모 클래스인 CAsyncsocket를 살펴보세요.)
4) 실제 게임회사의 면접에서 IOCP의 동작 메커니즘을 구두로 답변하기는 무척 어렵습니다.
그래서 저는 이렇게 묻습니다,
"왜 IOCP 소스에는 recv() 함수가 두번 적혀있나요?"
여러분은 머라고 대답하시겠습니까?



내용은 맘대로 퍼갈수 있지만 동의없는 수정은 안되며 출처(http://www.gamedevforever.com/ , http://rhea.pe.kr/ )를 명시해주세요.

 

 

출처 : http://rhea.pe.kr/494

+ Recent posts