메세지 관리를 위한 스트림 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#ifndef __STREAM
#define __STREAM
 
namespace MySream
{
    class CStream
    {
    public:
        CStream(VOID);
        ~CStream(VOID);
 
    private:
        BYTE *mBufferPointer;
        DWORD mLength;
 
    public:
        BOOL SetBuffer(BYTE *buffer);
 
    public:
        template <typename T>
        BOOL ReadData(T* Data)
        {    
            CopyMemory(Data, mBufferPointer + mLength, sizeof(T));
 
            mLength += sizeof(T);
 
            return TRUE;
        }
 
        template <typename T>
        BOOL WriteData(T Data)
        {
            CopyMemory(mBufferPointer + mLength, &Data, sizeof(T));
 
            mLength += sizeof(T);
 
            return TRUE;
        }
 
        DWORD GetLength(VOID)
        {
            return mLength;
        }
    };
 
    class CStreamSP
    {
    public:
        CStreamSP(VOID)  { Stream = new CStream(); }
        ~CStreamSP(VOID) { delete Stream; }
 
        CStream* operator->(VOID) 
        {  return Stream; }
        operator CStream*(VOID)  
        {  return Stream; }
 
    private:
        CStream    *Stream;
    };
}
 
#endif


'게임개발공부 > 무작정퍼오기' 카테고리의 다른 글

컨테이너 종류 복습  (0) 2013.12.21
텍스처 아틀라스  (0) 2013.12.20
맴버함수를 쓰레드로 쓰는법  (0) 2013.11.27
assert 사용법  (0) 2013.11.27
<소켓 서버> sockaddr  (0) 2013.11.25
Posted by JJOREG

소켓이란 무엇인가? -------------------------------------------------------------------------------------------

소켓이란 멀리있는 대상과 나를 연결해주는 통로 같은 존재. 전화기 같은 존재이다.

전화기로 통화를 하려면 상대의 전화번호를 알아야 하는 것처럼 소켓으로 컴퓨터간 네트워크 교류를 하려면 서로간의 IP주소를 알아야 한다.

위의 명령어는 연결을 요청하는 함수와 그 헤더파일이다.

위의 명령어는 연결요청을 수락한다는 함수이다.

  이와 같이 소켓 프로그래밍도 어려울것이 없다.

  전화기를 한대 구입하고, 전화번호를 할당한다음, 전화를 케이블에 연결하고, 전화를 기다리다가, 전화가 오면 전화기를 받고 대화를 하는 것이라고 보면 된다.


클라이언트? 서버? -------------------------------------------------------------------------------------------

일단 클라이언트와 서버의 기본적인 개념부터 이해해본다.

클라이언트란 여러분이 직접 플레이를 하고 있는 컴퓨터라고 보면 된다.

반대로 서버란? 여러분이 접속을 요청하는 게임회사에 존재하는 커다란 컴퓨터라고 보면된다.

일반적으로 클라이언트 쪽에서 대부분의 연락을 취한다.

게임 접속하겠습니다. 공격하겠습니다. 아이템 구매하겠습니다. 이동하겠습니다. 대부분의 요청을 클라이언트에서 요청하고 서버에서는 그것을 받아서 현재 클라이언트에 존재하는 객체의 값을 기록하거나 데이터를 변경하는 역할을 한다.


파일 입출력 -------------------------------------------------------------------------------------------

서버에서는 요청에 대한 대상을 파일로 간주하는 경우가 많다. 리눅스 기반이건 윈도우즈 기반이건 이는 다르지 않으며 시스템 수준에서 이러한 파일 입출력 조작함수를 지원한다.

리눅스에서는 이것을 파일디스립터라고 부르며 윈도우에서는 핸들이라고 부른다. 핸들은 윈도우즈 시스템 프로그래밍을 읽어본 나로서는 이해가 가는 부분이며 리눅스에서는 그것을 파일디스크립터라고 부른다고 이해하기로 했다. 즉 클라이언트에서는 항상 서버에게 이런 요청을 한다고 보면 된다.

내 파일(혹은 소켓)의 XX줄의 XX번부터 XX번까지를 참조해줘. 복사해줘....

당연히 이런 파일을 조작하기 위해서는 파일에 접근할수 있어야 하고 접근한다는 것은 그 파일을 열어서 조작한다는 것을 의미한다.

이런 명령어를사용해서 파일을 열어서 조작하여 클라이언트에 변화를 주는 것이다.

path 는 당연히 파일의 경로를 의미한다. 파일의 경로를 모르면 어떤 파일에 접근할지 알수가 없기 때문이다.

두번째로 flag는 파일의 모드를 의미한다.

방금 말했던대로 윈도우에서는 핸들이라고 하고 리눅스에서는 소켓 혹은 파일이라고 지칭했다. 윈도우에서 커널오브젝트나 핸들은 사용후엔느 닫아줘야 했다. 어느 시스템이건 이는 공통적으로 적용된다.

열고 닫는 함수가 있다면 당연히 쓰기 위한 함수도 있을 것이다.

fildes 데이터의 전송 영역을 나타내는 파일스크립터

buf 전송할 데이터를 가지고 있는 버퍼의 포인터

nbytes 전송할 바이트 수를 의미한다.

쓰는 함수가 있다면? 당연히 읽는 함수도 존재한다.

fildes 데이터의 전송해 주는 대상을 가리키는 파일스크립터

buf 수신 한 데이터를 저장할 버퍼를 가리키는 포인터

nbytes 수신할할 바이트 수를 의미한다.

여기까지는 리눅스 기반 일반적인 소켓과 파일 함수였다면 이번에는 윈도우즈의 소켓에 대해서 알아보자.


윈도우즈 소켓 기본 함수 및 프로그래밍 --------------------------------------------------------------------------

일단 알아두어야 할것이. 윈도우즈 소켓은 상당부분 BSD 계열 유닉스 소켓을 참고로 설계되어 있다는 것이다. 따라서 대다수의 부분이 리눅스 기반 소켓과 유사하다.

관련 함수
-------------------------------------------------------
1. 소켓을 생성하는 함수

SOCKET socket(int af, int type, int protocol);

성공시 소켓 핸들을 반환한다. 실패시 INVALID_SOCKET를 반환한다.

-------------------------------------------------------

2. IP주소와 PORT번호의 할당을 목적으로 호출하는 함수  

int bind(SOCKET s, cosnt struct sockaddr* name, int namelen);

-------------------------------------------------------

3. 소켓이 클라이언트의 프로그램의 연요청을 받아들일 수 있는 상태가 되게 하는 것을 목적으로 호출되는 함수.

int listen(SOCKET s, int backlog);

-------------------------------------------------------

4. 클라이언트 프로그램에서 연결요청을 수락할때 호출하는 함수.

SOCKET accept(SOCKET s, cosnt struct sockaddr* addrint addrlen);

-------------------------------------------------------

5. 클라이언트 프로그램에서 소켓을 기반으로 연결요청연결 요청을 할때 호출하는 함수

int connect(SOCKET s, cosnt struct sockaddr* name, int namelen);

-------------------------------------------------------

6. 소켓을 닫을때 사용하는 함수

INT closesoket(SOKET s);


소켓 과 핸들 ------------------------------------------------------------------------------------------------

윈도우에서는 시스템 함수의 호출을 통해서 파일을 생성할때 핸들을 반환한다. 윈도우는 리눅스와는 달리 파일 핸들과 소켓 핸들을 구분하고 있으며 핸들이라는 관점에서 바라볼 때는 동일하다고 바라볼수 있지만. 완벽하게 동일하지 않다.


윈도우 기반 입출력 함수 --------------------------------------------------------------------------

-------------------------------------------------------
1. 데이터 전송 함수

int send(SOCKET s, cosnt char * buf, int len, int flags);

s : 데이터 전송 대상과의 연결을 의미하는 소켓의 핸들 값 전달.

buf : 전송할 데이터를 저장하고 있는 버퍼의 주소 값 전달.

len : 전송할 바이트 수 전달.

flags : 데이터 전송 시 적용할 다양한 옵션 정보 전달.

-------------------------------------------------------

2. 데이터 수신 함수

int bind(SOCKET s, cosnt struct sockaddr* name, int namelen);

s : 데이터 수신 대상과의 연결을 의미하는 소켓의 핸들 값 전달.

buf : 수신된 데이터를 저장할 버퍼의 주소 값 전달.

len : 수신할 수 있는 최대 바이트 수 전달.

flags : 데이터 수신시 적용할 다양한 옵션 정보 전달.

-------------------------------------------------------

Posted by JJOREG

메모리의 범위와 종류 -----------------------------------------------------------------------------------------

1. 메인메모리

  메인메모리인 RAM을 의미하며 정확히는 D-RAM이다.

2. 레지스터

  CPU안에 내장되어 있는 레지스터를 의미한다.

3. 캐쉬

  D-RAM보다 빠른 S-RAM으로 구성되어있다. 캐쉬메모리는 원래 CPU의 일부로 존재하는 메모리 개념이 아니다. CPU에 근접해 있는 메모리 개념이다. CPU의 일부로 존재하는 메모리는 레지스터라는 것을 기억해야 한다.

  책의 설명 -> "CPU옆에 붙어있는 메모리"


메모리의 범위와 종류 -----------------------------------------------------------------------------------------

그림의 피라미드 구조가 메인메모리 구조이며 계층구조를 이루는 기준은 단 한가지로 명확하다.

"CPU로 부터 얼마나 떨어져 있는가?"


L1 L2캐쉬의 차이 -----------------------------------------------------------------------------------------

본래 CPU의 구조는 하나의 캐쉬만을 사용했다. 하지만 메인메모리는 캐쉬에 비해서 많이 느렸고 캐쉬의 요구를 메인메모리에서 수용하여 데이터를 입출력하는데 많은 시간이 들었다. 그리고 이러한 현상을 병목현상이라고 불렀다.

그러한 병목현상을 해결하기 위해서 캐쉬를 2개로 분할하여 메인메모리와 연결되는 L2캐쉬와 L1캐쉬로 나누어 놓았다. 그리고 중간과정을 하나 거침으로해서 병목현상을 개선하기 위한 용도로 사용한다.

그냥 하나의 큰 캐쉬를 쓰는것과 무슨 차이가 있는지에 대해서 까지는 책에서 설명이 되어있지 않지만 L2캐쉬에는 L1캐쉬에서 자주 참조하는 데이터를 비치해 놓는다는 차이를 두고 있다.


템퍼럴 로컬리티, 스페이셜 로컬리티 -----------------------------------------------------------------------------

우리가 사용하는 프로그래밍 코드에는 2가지 자주 발견되는 특징이 있다.

다음의 코드를 보자.

int* c[100] = new int;

n = 100;

c -> 초기화(100개의 랜덤한 정수를 부여한다.)

for(int i = 0 ; i < n ; i++)

{

for(int j = 1 ; j < n-1 ; j++)

{

int temp = c[j-1];

c[j-1] = c[j];

c[j] = teamp;

}

}

코드를 확인해보면 알겠지만 i, j, temp에 굉장히 자주 접근하고 있는 코드이다.

(Temporal Locality)템퍼럴 로컬리티란 프로그램 실행시 한번 접근이 이뤄진 주소의 메모리 영역은 지속적으로 자주 접근한다는 특징을 의미한다.

두번째 특징은 j-1과 같이 메모리 영역을 4바이트씩 지속적으로 줄여가며 접근을 하고 있다.

(Spatial Locality)스페이셜 로컬리티란 프로그램 실행시 접근하는 메모리 영역은 이미 접근이 이루어진 영역의 근처일 확률이 높다는 것을 의미한다.

당연히 이러한 2가지 특성은 캐쉬의 성능을 향상시키고 이러한 캐쉬메모리의 연산을 줄여주는 코드를 캐쉬 프랜들리 코드라고 한다.


캐쉬 힛, 캐쉬 미스 --------------------------------------------------------------------------------------

만약 L1캐쉬에서 자신의 메모리를 검색하여 필요한 데이터가 존재하면 이를 캐쉬 힛(Cache Hit)이라고 한다. 반대로 없을 경우에는 L2캐쉬에서 메모리를 가져와야 하는데 이를 캐쉬 미스(Cache Miss)라고 한다.

만약 이때 L2에서 데이터를 보낼때 블록단위의 전송을 한다. 즉 필요한 데이터를 4바이트 하나만 보내는 것이 아니라 그 주변 일정 블럭 이내의 데이터를 한번에 보내는 것이다. 이러한 점 때문에 스페이셜 로컬리티나 템퍼럴 로컬리티의 특성이 연산에 영향을 주게 되는 것이다.

또한 당연히 새로운 데이터를 출력하면 캐쉬교체가 일어나게 되는데 이때.

캐쉬 교체 정책(Cache's Replacement Policy)의 기준에 따라서 블럭단위 이동과 함께 블럭단위로 데이터를 밀어내게 된다. 그리고 가장 보편화된 기준은 LRU(Least_RecentlyUsed)알고리즘 이다.

단순히 이야기해서 가장 오래전에 참조한 메모리블록을 밀어내고 새 메모리 블록을 그곳에 새로 로딩하는 것을 의미한다.


가상메모리, 물리주소 -----------------------------------------------------------------------------------------

컴퓨터의 Ram은 512M바이트인데 어떻게 프로세스에는 4G가 할당될까?

C++에서 포인터의 주소는 4G가 한계이다. 물리주소는 512M바이트 안으로만 설정될 수 있다. 그렇다면 메모리 주소를 볼때 확인되는 물리적 주소를 넘어가는 주소는 어떻게 할당되는 것일까?

간단히 이야기 해본다면 모든 메모리가 꽉곽 채워져서 할당되지 않는 다는 점이 그 문제의 해결책이 된다.

MMU(Memory Management Unit)라는 녀석이 존재하여 메모리 할당을 중간에서 물리적 한계보다 더 크가 잡아서 할당하는 것이다.


MMU는 메모리의 일부분을 확인하고 그 일부분을 가상메모리 주소로 할당하여 메인메모리에 저장한다. 이로서 주소의 문제는 해결되었다. 하지만 물리메모리가 정말로 꽉 차버린다면 어떻게 해야할까?



바로 위와 같이 일부 데이터는 하드로 이동시키고 새로운 공간을 메인메모리에 할당하게 된다.

이를 통해서 어떻게 2개의 프로세서에서 각각의 프로세서에 4기가 이상의 바이트가 할당 가능한지 설명이 가능해진다. 물론 MMU가 이 모든걸 처리하는 것은 아니다. 가상메모리에서 메모리 할당과 주소 변환에 관련된 것들만을 MMU가 처리한다. 이러한 하드웨어적인 부분을 제외한 소프트웨어적인 부분은 운영체제와 관련이 있으며 이 부분은 윈도우 시스템에서 VMM(Virtual Memory Manager)과 관계가 깊다.

Posted by JJOREG