choiwonwoo.egloos.com
U a Thinking? U a Dream? D a Passion?

개발자마을

by choiwonwoo
카테고리
My Story & Article
My Thi/Dre/Pas
Dev Article
Dev Information(C#)
Dev Reference Site
Dev Security
Dev Web
Dev Tool
Dev Database
Dev Network
DEV C
Dev Linux
ServerDevResource
JAVA
Powered by egloos
rss

skin by 이글루스
Egloos | Log-in
Web Session ID
앞서 해킹기법에서 스니핑(sniffing)에 대해서 살펴보았다. telnet, ftp, pop3 등의 비암호화 프로토콜 어플리케이션은 스니핑 공격을 통하여 사용자 계정 및 암호 도용에 취약할 수 있음을 알게 되었다. 마찬가지로 우리가 웹 브라우징시 사용하는 HTTP 프로토콜도 이러한 도용에 취약할 수 있다.

HTTP Session Hijacking(혹은 Session ID Hijacking)이라는 공격 기법은 웹 브라우징시 세션 관리를 위해 사용되는 Session ID를 스니핑이나 무작위 추측 공격(brute-force guessing)을 통해서 도용하는 기법이다. 먼저 이러한 공격에 대한 기초적인 배경지식으로 HTTP 프로토콜의 특성 및 Session ID에 대해 이해해보도록 하겠다.


HTTP 프로토콜의 특성

HTTP는 기본적으로 비연결유지(stateless) 프로토콜이다. 반면, telnet과 ftp와 같은 프로토콜은 클라이언트와 서버 사이에 하나의 연결(session)이 성립되어 통신하는 프로토콜이다. 따라서, 우리가 보통 웹 브라우저를 열어 URL을 입력하고 해당 홈페이지에 들어간다는 것은 해당 홈페이지에 포함되어 있는 페이지(html), 그림(jpg, gif 등), 자바스크립트(js) 등을 다운받기 위해 개별적인 여러 개의 80 요청(request)을 발송한 후 서버로부터 각각의 응답(reply)

을 받는 것을 의미한다.

이러한 일련의 요청과 응답이 이루어진 후 해당 서버와의 통신은 다시 종료된다. 위와 같은 기본적인 지식을 알고 있다면 다음과 같은 질문을 할 수 있다. HTTP는 비연결유지 프로토콜이라고 하였는데 Session Hijacking 이란 공격은 어떻게 가능한 것인가? 이는 HTTP 세션 관리를 위해 사용되는 Session ID를 통해서 가능하다.


Session ID란 무엇인가?

웹 서버는 다수의 웹 페이지 요청자를 구별하기 위하여 각각의 사용자의 세션에 대해서 임의의 긴 문자열 값인 Session ID를 부여한다. 사용자가 홈페이지 방문시 혹은 인증 로그인시에 생성된다. 이러한 Session ID는 사용자의 계정, 암호, 그 밖의 IP 주소, timestamp 등의 여러 파라미터들을 조합하여 생성할 수 있다.


Session ID는 사용자와 일련의 웹 서핑 동작을 연결시켜줌으로써 웹 사이트 로그인 후 다른 페이지 방문시마다 매번 로그인을 하지 않아도 되는 편리함을 제공해준다.

우리가 신문 홈페이지나 포털 사이트에 들어갈 때 광고 배너가 자동으로 바뀐다던지, 쇼핑몰이나 인터넷 서적몰에서 구매 카트의 목록이 유지되는 것은 모두 이러한 원리이다. 즉, Session ID를 통해 인증과 인가(authentication
& authorization)라는 세션 관리를 수행할 수 있다.


Session ID는 어디에 존재하는가?

Session ID는 우리가 흔히 듣는 쿠키(cookie)라는 곳에 저장되는 것이 일반적이다. 그러나 가끔은 웹 브라우저 주소창 URL이나 HTML 페이지 폼 소스 상의 hidden 필드에 포함되어 드러나기도 한다.

1)

쿠키


2)

웹 브라우저 주소창의 URL


3)

웹 페이지 폼 소스 상의 hidden field



Session ID의 취약성은 무엇인가?

웹 서버에서의 Session ID 생성 기법 및 관리 기법에 따라서 다음과 같은 취약점이 존재할 수 있다.

  • 강력하지 못한 알고리즘(Weak Algorithm)

    : session ID 스트링 값을 생성함에 있어서 공격자가 reverse 엔지니어링이 가능한 쉬운 알고리즘으로 생성될 경우 cracking이나 brute-force guessing 공격의 위험이 있다.

  • 길이가 짧은 Session ID : 강력한 암호 알고리즘을 사용하더라도 그 길이가 충분하지 않고 짧은 경우에는 cracking이나 brute-force guessing 공격의 위험이 있다.

  • 계정 잠금 기능 미비 : 로그인 패스워드의 특정 회수 실패에 대해서는 보통 계정잠금 기능이나 해당 IP 차단 기능을 구현하고 있습니다. 그러나 보통 Session ID에 대한 무결성 침해나 특성 회수 실패에 대해서는 이러한 잠금 기능 구현이 미비하다. 따라서, brute-force guessing 공격의 위험이 있다.

  • 무한 만료의 Session ID : 사용자의 로그 아웃 이후에도 서버측에서 해당 세션 ID값을 폐기하지 않고 무한정 유효 인정한다면 cookie sniffing이나 프락시 서버의 로그 취득을 통하여 session ID 공격이 가능하다.

  • 평문으로 전달되는 Session ID : 서버에서 클라이언트로의 session ID 쿠키 전달 방식이 비암호화 방식일 경우에는 sniffing을 통하여 해당 값이 노출되어 공격 받을 수 있다. 특히 Session ID 값 자체가 사용자명이나 암호 등의 평문으로 구성되어 있는 경우에는 직접적인 공격이 가능하다.

    위와 같은 취약성에 대한 Session ID 공격의 유형은 다음과 같다.


    Session ID 공격유형

  • 직접적인 Cookie Sniffing을 통한 Session ID 도용

  • 간접 우회 공격을 통한 Session ID 도용

  • Brute-force guessing을 통한 Session ID 도용

    지금까지 Session ID가 무엇인지, 어떤 형태로 존재하는지, 왜 취약한지에 대해서 알아보았다. 다음에는 실제 공격 유형에 대해 살펴보고, 대응 방안에 대해서도 논의해 보도록 하겠다.

  • # by choiwonwoo | 2007/08/29 12:02 | Dev Web | 트랙백 | 덧글(0)
    [펌] Linux Network Engine for Online Game
    문서상태 작성: 이홍기(orinmir _at_ gmail.com)
    등록일: 2003년 4월 20일
    문서버전: 0.1.1
    수정일:
       v0.1.2 ; 2003-04-21; 문서 저작권 수정

    일러 두기 : 본 문서는 select와 rtsignal사이의 구현상의 차이점과 온라인 게임에 적합한 서버의 구조를 간단하게 정리한 문서이다. 실제로 구현을 해 본 바로는 /dev/epoll이 rtsignal보다는 좀더 안정적인 것 같았다. 그 부분에 대해서도 갱신해 나갈 예정이다.
    받기   * linux network game engine for rtsignal (doc)
      * rtsignal patch: one-sig-perfd-2.4.19.patch, one-sig-perfd-2.4.13.patch
      * /dev/epoll patch : ep_patch-2.4.19-0.32.diff, ep_patch-2.4.18-0.32.diff
    내용 Linux RealTime Signal 기반의 온라인 게임 서버 엔진의 구현

    문서 위치: http://chonga.pe.kr/computer/linux/linux-netengine/

    최초 작성일: 2002-11-05

    최종 수정일: 2003-04-21


    목차

    0. 머리말

    1. 개요 및 소개

    2. 이론 및 배경

    3. 구현 과정

    4. 결론 및 검토

    5. 참고 문헌

     

     

    0. 머리말

     본 문서의 저작자는 이홍기(orinmir _at_ gmail.com)이며, 이 곳에 사용된 자료의 내용과 자료의 그림에 대한 발췌 및 수정 후 재 배포시 저자에 대한 정보를 포함해야하며 그 때에는 저자의 허락을 받도록 한다.

     

    1. 개요 및 소개

      Linux는 OS 개발의 역사에 있어서 Open source project의 견인차 역할을 하고 있는 OS로 embedded system에서부터 고성능 서버의 OS에 이르기까지 주목 받고 있다.

      온라인 게임은 현대 게임 산업의 화두다. 지역 네트웍이나 30명 미만의 동시 접속으로만 이루어진 형태의 초기 온라인 게임에서 지금은 동시접속 1만명 이상을 처리하는 대용량 온라인 게임의 등장으로 관련 분야에 대한 관심이 높다. 실제로 온라인 게임 서버는 일반적인 네트웍 기술을 기반으로 특화된 형태의 독립서버라 볼 수 있다.

     

      온라인 게임 서버의 범주에서 볼 때, 고성능이면서 대용량의 처리를 하려면 패킷의 송수신 처리부와 데이터 처리부의 최적화가 핵심이며 이에 대한 여러가지 구현 방법이 존재한다. TCP/IP 기반의 네트웍 환경에서 일반적으로 사용되는 BSD socket의 고전적인 구현 방법으로는 select/poll을 이용한 io multiplexing(혹은 polling 방식)이 오래된 만큼 안정적이다. 하지만 이 방법은 말 그대로 kernel에서 처리되는 network io를 주기적인 polling에 의해 감시하므로 고성능 대용량 서버에서는 불필요한 CPU 부하를 유발하며 분명한 한계가 존재한다. 이에 대한 대안으로 여러 가지 vendor별 개선안이 나와있으며, linux에서는 RT Signal을 사용한 개선방법이 제공된다. 본 글에서는 이 RT Signal 방식을 이용한 네트웍 처리부를 통해 기존 방식에 비해 개선된 소켓 서버를 이용해 게임 서버의 기본 엔진을 구현에 대해 정리하고자 한다. 성능에 대한 평가는 추후 작업으로 남겨둔다.

     

    2. 이론 및 배경

      BSD socket의 태동과 함께 하나 이상의 소켓 접속을 처리하는 서버에서 사용된 방법은 io multiplexing 방식으로 select()와 poll() 시스템 콜을 통한 polling 방식이었다. 이 방법은 지금까지도 unix 서버 프로그램의 대부분에서 사용되는 방식 중의 하나이며 전통적인 unix 프로그램은 이 방식과 함께 프로그램의 구조 역시 single thread을 선호하였다. Unix kernel은 초기에 multi threaded application에 대한 지원이 없이 설계되었기 때문이기도 하다.

      현재에 이르러서는 unix에서도 SMP(Symmetric Multiprocessing) 시스템의 범용화와 더불어 posix thread를 대부분 지원하고 있고, 이와 더불어 전통적인 polling 방식이 시스템 자원의 낭비를 초래하고 있어 개선점을 찾으려는 시도가 많아졌다. 이를 해결하기 위해 표 1)과 같이 여러가지 개선 방식을 vendor 별로 지원하고 있으며, 최근에 들어서 linux에서도 여러 가지 패치를 통해 새로운 socket 처리 방식을 지원하고 있다.

     

    표 1) 소켓 서버의 vendor별 개선 방식

     

    개선 방법

    설명

    안정성

    MS

    Windows

    IOCP(io completion port)[iocp]

    GetQueuedCompletionStatus()

    호출을 통해 커널로부터 직접 socket event queue를 받음.

    대부분의 상용

    게임서버에서 채용

    Solaris

    /dev/poll

    [devpoll1]

    Poll()방식의 완벽한 대체, 장치 개념으로 처리

    sun에서 지원

    FreeBSD

    kqueue/kevent

    [kqueue]

    event poll과 유사

    안정적이라는 평

    Linux

    Realtime signal[rtsig1]

    Sigwaitinfo()호출을 통한 POLL_IN event 받음

    Signal overflow

    단점

    Realtime signal/fd

    Realtime signal 방식과 같으나

    Signal overflow 문제 해결

    몇가지 웹서버에서

    테스트 됨

    /dev/poll

    Solaris /dev/poll과 유사

    Sun의 /dev/poll

    보다 성능 월등히 떨어짐[devpoll2]

    /dev/epoll [rtsig2]

    Solaris /dev/poll과 유사

    현재 테스트 코드 나옴 /dev/poll보다 성능이 뛰어남.

     

      각 vendor별 지원 방식은 대동소이하며 방식은 커널로부터 관심 있는 socket 중, 이벤트가 있는 것만 받아오는 식이다. 기존의 polling 방식이 갖는 문제점은 single thread 기반이어서 timeout 시간을 두면서 looping을 하도록 되어 있다는 점이다. timeout 이후의 시간에 다른 processing을 할 수 있도록 하는 것이 일반적이다. 이와 더불어 최대 1000개의 소켓 접속을 받을 수 있다면, 1000개의 소켓에 대해 매 시간 polling을 하여 event의 발생 유무를 확인하도록 되어 있다.

      그림 1)과 같이 polling 방식은 Kernel space에서 가진 1000개의 소켓 정보를 그대로 User space로 복사해 온 후, 전체 목록을 들면서 비교하는 방식을 취한다. 높은 성능을 내기 위해서는 timeout 시간을 적게하여 검사시간을 짧게하여 Packet 전송, 수신 효율을 높여야하는 문제점과 사용하지 않는 소켓 정보를 모두 검사해야하는 문제점이 있다. 따라서 그림 2)와 같이 일반적으로 이 구현을 고성능 서버에서 사용하는 경우 1개의 소켓 접속만  이루어져도 이 프로세스에 의해 CPU 사용률이 90% 이상으로 증가하며, 소켓 접속이 많아지면 많아질수록 응답 속도도 떨어질 가능성이 있다.

     

    그림 1) polling 방식(select() )과 RTsignal 방식의 비교

     

    그림 2) polling 방식의 구현 예

      본 글에서 Linux의 RTSignal을 도입하게 된 것은 기존 방식의 대안 중 하나이면서 linux kernel 2.2부터 RealTime Signal(Posix RealTime Signal)을 지원하기 시작했기 때문이다. Linux의 RT signal방식은 과부하 상황에서 overflow가 일어나는 기존 signal 기반 프로그램들보다는 명확하게 polling 방식을 대체할 수 있는 방법이다. (poll()에서 사용하는 POLL_IN, POLL_OUT, POLL_ERR 등을 그대로 사용할 수 있다.) RT Signal의 장점이라면, signal을 등록해놓고 해당하는 이벤트가 발생하기 전까지는 sigwaitinfo system call을 통해 대기하고 있다가, 이벤트가 발생하면, 정해둔 signal handler를 통해 처리를 하며 timeout도 지원한다. 기존의 구현 방식에 비해 낮은 CPU점유율과 빠른 응답 속도, high load에서도 안정되고 일관된 성능을 유지하고 있다. 이러한 방식을 또한 event-dispatch 방식이라고도 한다. [rtsig3]

      하지만 기본적으로 제공되는 RT Signal의 단점은 제한된 signal resource로 인해, 고부하에서 signal overflow가 되면 더이상 새로운 접속을 처리할 수 없게 된다. 이 때에는 기존의 poll()쪽으로 처리를 돌려 해결하는 혼합된 구현 방법을 함께 쓰기도 한다. 기본적으로 linux에서 지원하는 방법은 기존의 polling 방식과 병행해야하는 문제점이 있다.[phhttpd] 최근에 이르러 이를 극복한 방법으로 1개의 file descriptor당 하나씩 signal을 할당하고 그 개수 만큼 가지게 하여, 자원 만큼을 수용할 수 있는 방식의 패치가 Luban에 의해 제안되었다. 이 프로젝트에서는 이 방식을 사용하여 작성하기도 결정하였다.

      타 OS의 개선 방법에 비해 linux에서 새로 도입된 이 방식은 EWOULDBLOCK 등에 대한 에러 처리를 지원하지 않기 때문에 이에 대한 것을 설계 시점에서 같이 해주어야 했다. 따라서 그림 3)과 같은 producer/consumer구조를 도입했다. 우선 IO Event Queue를 두어 같은 형태로 POLL_IN, POLL_OUT, POLL_REMOVE를 처리하도록 하였는데, 이 구조는 INPUT event는 sigwaitinfo()의 호출에서 POLL_IN에 해당하는 socket read event가 발생하였을 경우에 wake up이 되고 IO Event Queue에 push함으로써 thread의 기반 구조에서 producer의 역할을 하게 된다. 마찬가지로 게임 서버가 실행되면서 이벤트가 발생하는데 이때 POLL_OUT이라면 이에 해당하는 socket write event로 정의하고 객체의 object가 제거되면 POLL_REMOVE에 해당하는 socket close event를 발생시켜 IOEvent Queue에 producer thread의 역할을 하게 된다. Consumer thread는 Event Dispatching Thread를 통해 IO Event Queue를 pop을 하여 각각의 경우를 read(), write(), close()에 대응시켜 이벤트를 처리하며, 각각의 시스템 콜에서 EWOULDBLOCK과 같은 socket error를 만나면 또 다시 IO Event Queue에 그 상황에 맞는 Event를 만들어 다시 넣어주는 과정을 통해 소켓의 reading/ writing buffer 제어를 해준다.

    그림 3) RealTime Signal I/O의 구현

      결론적으로 RTSignal 방식을 사용함으로써 기존 방식에 비해, 시스템 자원을 효율적인 활용 할 수 있고, event dispatch방식으로 threaded application에 적합한 구조로 쓰여질 수 있다. 예상되는 문제점으로는 threaded application이 갖는 성능 공유 객체에 대한 thread간 경쟁에 대한 성능 배분을 어떻게 최적화하느냐에 따라 성능이 크게 좌우될 가능성이 높다.


    3. 구현과정

     

      앞서 설명한 RT signal을 이용한 소켓 서버의 개선 점을 토대로 polling 방식과 RealTime Signal을 이용한 I/O 부분을 각각 구현하여 기본 소켓 서버를 구현한다. 게임 서버의 기본이 되는 게임 객체 처리와 Packet 분석 및 처리 과정을 통한 게임 서버 엔진을 만들고 엔진 성능 테스트를 위한 더미 클라이언트를 만든다.

     

    개발 환경

     

    Server

    Client

    비 고

    OS

    Linux

    Kernel 2.4.19

    Microsoft Windows

     

    Compiler

    Gnu c/c++ 2.95.2

    Borland C++ Builder 5

     

     

     Server 의 설정

     Server에서 linux kernel은 2002년 11월 25일 현재 최신 안정 버전인 2.4.19를 사용하였다.[liinuxknldn] 대부분의 network server는 socket을 기반으로 하고 있으며, kernel에서 설정하는 socket 개수은 file descriptor의 한계에 따라 제한된다. 따라서 이에 대한 kernel 설정 작업은 필수적이다. 또한 Realtime signal을 사용하기 위해서는 Realtime signal per File descriptor 에 대한 패치를 해야한다. Kernel의 설정과 함께 /dev/epoll 에 대한 세팅도 할 수 있으며 참고적으로 추가했다.

     

     

    내 용

    Max socket 설정

    에 대한

    Kernel 수정

     

    include/linux/fs.h:

      increase NR_FILE from 4096 to 65536 

      increase NR_RESERVED_FILES from 10 to 128

     

    fs/inode.c:

     increase MAX_INODE from 16384 to 262144 

    참고: MAX_INODE는 NR_FILE의 3배정도 (2.4.19에서 max없음)

     

    Include/linux/limits.h

      NR_OPEN->15000

    Rtsignal per fd

    Luban ‘s patch 적용 [rtsigdn]

    dev/epoll

    Eventpoll 적용 [epolldn]

    Realtime Signal과 더불어 eventpoll도 함께 패치를

    적용하고 아래와 같이 char 장치를 하나 만든다.

    mknod /dev/epoll c 10 124

    Sysctl 설정

    echo 20 > /proc/sys/net/ipv4/tcp_fin_timeout

    echo 8192 >  /proc/sys/net/ipv4/tcp_max_syn_backlog

    echo 1200 > /proc/sys/net/ipv4/tcp_keepalive_time

    echo 1 > /proc/sys/net/ipv4/tcp_syncookies

    Shell 설정

    Shell에서 설정한 socket max에 대해 재설정을 해주어야한다

    $ ulimit –n 15000

     

     

      프로젝트는 크게 Server Engine과 ClientEngine 부분으로 나눌 수 있다. (표2) 참조.) 프로젝트의 이름은 물고기 등을 몰면서 논다는 의미의 몰이와 놀이의 합성어로 MoriNori(몰이놀이)로 지었다.

     

    표 2) 프로젝트 모듈 설명

    프로젝트 폴더

    내용

    파일 명

    ServerEngine

    자료구조 및 Packet 정보 등 의 공유 라이브러리

    Queue* , DLinkList*, BST*

    MAP은 n x n 2차원 좌표계 구조로 지원

    Map*

    Packet 처리부

    PacketParser*

    소켓과 소켓서버에 대한

    라이브러리

    Socket*

    SocketServer*

    Network*

    게임 기본 객체의 처리

    ServerBasic*

    ClientEngine

    서버와 네트웍 쪽의 기본라이브러리는 공유하며 더미 접속클라이언트 기능과 객체 동기 테스트 기능

    *

     

     그림 3)의 Realtime signal의 I/O를 Network 라이브러리에 구현을 한 상태로, 사용자 접속을 받아 소켓 버퍼를 제어하게 되면, 그 다음으로는 Game에서 쓰이는 Packet 형태로 가공을 하게 되는데, 그림 4)에서 Network Module에서 Input Packet과 Output Packet의 형태로 CPacket class를 통해 게임으로 들어오고 나가는 Packet이 관리된다. 이 때, 사용자로부터 입력된 Packet을 실제로 소비하는 부분이 Packet Parser이며 Game Object Loop에서 다시 사용자에게 보내지는 Output Packet을 발생시킨다. Packet Parser를 통해 유입된 Packet은 Game Object Loop를 통해 PC(Playerable Character, 사용자가 조종하는 객체) 객체의 행동을 담당하게 되며, 컴퓨터 인공지능으로 PC가 아닌 다른 MONSTER 객체를 처리하게 된다. 또한 Game Global Loop에서 게임내에서의 날씨 변화 등에 대해 처리하며, Output Packet에 영향을 미친다. 여기까지 2가지 Game Loop가 등장하는데 이 것에는 tick이라는 단위가 도입되는데, 이 tick은 한 Loop당 1/10 sec 와 같은 현실적인 Loop 수행에 관련된 응답 시간이 요구된다. 이 tick을 통해 게임내 객체는 tick단위의 동기화가 일어난다.

    그림 4) 게임 서버의 구조

      게임 클라이언트의 구조는 게임 서버의 구조와 어느 정도는 일치하지만, 대용량의 사용자를 처리하는 개념이 아닌 나 자신이 조종하는 PC 객체의 시야 범위 내에서의 객체 이동에만 관심이 있으므로 네트웍 부분과 Packet 처리에 있어서는 간소화 된다.

     

    그림 5) 게임 클라이언트의 구조

     

    서버 엔진의 구현

     

    [기본 라이브러리]

      프로젝트 초기에 기본적인 자료구조와 쓰레드 관련 함수들은 c++기반으로 작성하였다. 기본적으로 class기반의 SGI에서 나온 standard template library(stl)에서 자료구조를 제공하고 있지만 코드의 간명함과 threaded application에서의 저수준에서의 최적화, 실행 바이너리 사이즈를 줄일 수 있는 장점이 있어 대부분을 직접 구현 하였다. Thread에 관련된 라이브러리는 기존의 c thread library로 정의되어 있는 pthread_* 부분을 class로 감싸주었다.[pthread]

     

    클래스 이름

    설명

    비고

    CMutex

    객체 상호배제 lock/unlock

     

    CRWLock

    Read/Write Lock

     

    CCondition

    Condition 동기

     

    CThread

    Posix thread의 create

    Join, schedule 등을 감싼

    Thread 라이브러리

     

    CThreadWrapper

    Thread의 startup 동기 지원

    CThread를 감쌌다.

    CQueue

    Locked Queue

    CMutex

    Condition

    CQueueLockedPBuffer

    Multiple pop queue를

    지원한 Locked Queue

    기존 CQueue에 array로

    여러개를 pop

    CQueueBuffer

    BYTE Buffer처리를 위한

    Queue

    Socket Buffer로 사용

    CDLinkedList

    기본 적인 Locked List

     

    BST

    Locked Binary Searching Tree

     

     

     

    [네트웍 엔진의 구현]

      게임 서버에서의 네트웍 엔진은 일반적인 네트웍 서버의 형태를 따르며, 몇가지 측면에서 성능 개선 위한 방식을 사용한다. 기본적으로 본 프로젝트에서는 비교하고자하는 polling방식과 RTSignal 방식의 큰 차이가 있고, 의사코드에서 보는 바와 같이 client별로 socket을 Add/Delete해주는 Do_AddSocket(), Do_CloseSocket()과 buffering을 해주는 Do_Read(), Do_Write와 같은 부분은 서로 공유하게된다. socket을 accept하는 부분의 부하를 줄이기 위해 하나의 독립된 thread로 처리하였다.

     

     accept thread의 의사코드(pseudo-code)

    while(1)

    {

        s = accept ( serversocket)

        if (is_valid_socket(s) ) {

            Do_AddSocket(s);

        }

    }

     

     Polling 방식은 관심이 있는 Socket 정보를 서버에서 처리할 수 있는 MAXSOCKET의 FD list에 FD_SET 명령을 사용하여 세팅하고 select() 시스템호출을 통해 넘겨받은 결과 개수에 따라 다시 MAXSOCKET 만큼을 반복하면서 FD_ISSET으로 어떤 종류의 event인지 검사하는 구조다.

     

     select polling의 의사코드

    while(1)

    {

         // socket fdset recompute

    FD_ZERO(INPUTFD); FD_ZERO(OUTPUTFD); FD_ZERO(EXCEPTFD)

        For ( s=0 ;s<= MAXSOCKET;s++) {

             If (is_valid_client_socket(s) )  {

                 FD_SET(s,INPUTFD);

    FD_SET(s,OUTPUTFD);

    FD_SET(s,EXCEPTFD);

             }

        }

        resultcount = select (MAXSOCKET+1, INPUTFD, OUTPUTFD, EXCEPTFD, TIMEOUT{sec=0,usec=1000});

     

    For ( s=0 ;s<= MAXSOCKET && resultcount>0 ;s++) {

             If (is_valid_client_socket(s) )  {

                 If (FD_ISSET(s,INPUTFD)) {

    resultcount --;

                      Do_read(s); // 실패시 socket 삭제

                 }

    If (FD_ISSET(s,OUTPUTFD)) {

    resultcount --;

                      Do_write(s); // 실패시 socket 삭제

                 }

    If (FD_ISSET(s,EXCEPTFD)) {

    resultcount --;

                      Do_ReadOOB(s); // Out of Bound 긴급 패킷(거의 쓰이지 않음)

                 }

         } // for

    } // while

     

     RTSignal은 socket의 생성과 소멸시에 추가적인 작업이 필요하며 Signal 처리 thread에서Socket IO Event Dispatch에서 사용될 POLLIN/POLLOUT/POLLREMOVE에 해당하는 IOEvent를 생성하며, 게임 루프에서 클라이언트로 패킷을 만들어 내거나 강제로 삭제해야하는 경우 그때 마다 POLLOUT/POLLREMOVE IOEvent를 생성하게된다. 이렇게 생성된 IOEvent는 Event Dispatch Thread 의사코드와 같이 이를 통해 소비된다. 모든 IO Event Queue에서 pop 처리는 CQueueLockedPBuffer를 사용하며 array로 query하여 한꺼번에 처리해여 함수 호출에 대한 overhead를 줄이도록 하였다.

     

     RTSignal에서의 Do_AddSocket 호출시에 추가해야하는 정보

    fcntl(SOCKETFD, F_SETFL, O_RDWR | O_NONBLOCK | O_ASYNC);
    fcntl(SOCKETFD, F_SETSIG, SIGRTMIN);
    fcntl(SOCKETFD, F_SETOWN, getpid()); // pid는 sigwaitinfo가 일어나는 thread pid

    fcntl(SOCKETFD, F_SETAUXFL, O_ONESIGFD);

     

    RTSignal에서의 Do_CloseSocket 호출시에 추가해야하는 정보

    fcntl(SOCKETFD,F_SETFL,O_RDWR);

     

    RTSignal에서의 Signal 처리 Thread 의사코드

    While (1) {

    sigemptyset(&signalset);

        sigaddset(&signalset,SIGRTMIN);

        pthread_sigmask(SIG_BLOCK, &signalset, NULL); // pthread signal block 무시하기

    signum=sigwaitinfo(&signalset,&siginfo);

    if (signum==SIGRTMIN) {

         SOCKETFD=siginfo.si_fd;

         revents=siginfo.si_band;

         si_code=siginfo.si_code;

    if (si_code==POLL_IN) {

           if (revents & (POLLIN)) {

                insertIOEvent(SOCKETFD,RTSIG_EVENT_POLLIN)

           }

         else if (si_code==POLL_OUT) {

    if (revents & (POLLOUT)) {

                insertIOEvent(SOCKETFD,RTSIG_EVENT_POLLOUT)

           }

         }

         else {

    insertIOEvent(SOCKETFD,RTSIG_EVENT_POLLREMOVE)

         }

    }

    } // while

     

    RTSignal에서의 IO Event Dispatch Thread 의사코드

    While (1) {

    IOEvent = POP_IOEVENT_FROM_QUEUE();

        If (IOEvent.event == RTSIG_EVENT_POLLIN) {

            Do_Read(IOEvent.SOCKETFD);

        }

       else if (IOEvent.event==RTSIG_EVENT_POLLOUT) {

           Do_Write(IOEvent.SOCKETFD);

       } else {

           Do_CloseSocket(IOEvent.SOCKETFD);

       }

    } // while

     

     

    [서버의 객체 처리]

     빠른 응답 속도를 위해 UserID를 기반으로하는 BST(Binary Searching Tree)와 SocketID와 UserID에 대응하는 array를 통해 searching 최적화를 시도하였다. 2진 트리로 구성하면 검색 시간이 2분 검색과 같은 0(log2n)이 되고 자료의 집합이 유동적일 때 행렬이나 리스트를 이용하는 것보다 효율적이므로 사용자의 접속이 순차적인 순서가 아닌 유동적인 접속이 이루어지므로 사용자 객체의 목록 관리에 BST를 사용하였다.[ds]

     사용자의 객체를 찾기 위해서는 다음과 같은 과정을 거친다

     

    순서

    내용

    속도

    1

    Soc