'게임개발공부/무작정퍼오기'에 해당되는 글 13건

  1. 2013.11.27 assert 사용법
  2. 2013.11.25 <소켓 서버> sockaddr
  3. 2013.11.24 무작정 쳐보자 <CSTREAM>

Assert 가 뭔가?? 단정문??


2008년에 처음으로 TDD를 접했는데 단정문이라는 단어 그 자체가 확실히 정의 되지 않았습니다.

(assert문은 Junit에서 쓰는 거죠.. 혹은 xUnit에서..)

 

단정문?? 단정이라는 뜻이 뭐지??

 

단정이란말은 단정짓는다 라는 말에서 단정과 같은 의미라고 생각하면 편할것 같습니다. 그리고

TDD 책에서는 assert 시리즈를 ~이어야 함! 으로 해석하면 헷갈리지 않을 수 있다고 하더라고요.ㅋㅋ


· assertTrue([message], expected) : expected 값이 참이어야 함! 아니면 실패(fail)

   - [message] : assert문을 실행할때 나오는 메시지. 생략가능하지만 써주는것이 정말 좋은 습관임!!

      expected  : 예상하는 기대값.  


· assertEquals([message], expected, actual) : 기대값(expected)이 실제로 나오는 값(actual)과 같아야 함! 아니면 실패(fail)

  - [message] : assert문을 실행할 때 나오는 메시지.

      expected : 예상하는 기대값.

      actual       : 실제로 실행해서 나오는 값.


이외 assertFalse, assertSame, assertNotSame, assertNull, assertNotNull 등과

Hamcrest 의 assertThat이 있습니다.



사용예


계정을 나타내는 account 객체와 잔고(balance)의 getter 함수 getBalance() 가 있다고 할 때


assertTrue(account.getBalance() == 0): 계정(account)의 잔고(Balance)는 0이어야 함. 0이 아니면 실패!

assertEquals(0, account.getBalance()): 위에 설명한 assertTrue와 같은내용을 테스트할 때 쓸 수있는것으로   등호비교는 가급적 assertEquals을 쓸것은 권한다고 하네요.


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

컨테이너 종류 복습  (0) 2013.12.21
텍스처 아틀라스  (0) 2013.12.20
맴버함수를 쓰레드로 쓰는법  (0) 2013.11.27
<소켓 서버> sockaddr  (0) 2013.11.25
무작정 쳐보자 <CSTREAM>  (0) 2013.11.24
Posted by JJOREG

sockaddr 구조체

  소켓 주소를 표현하는 구조체다. 아래 정의를 보면 주소 체계와 주소 두가지 정보만 갖고 있는 단순한 구조로 되어있다. 원래 소켓 자체가 TCP/IP만을 목적으로 만들어진 것이 아니어서, 다양한 주소 체계에 맞게 범용 목적으로 사용하기 위해 이런 구조를 가지고 있다.

 

struct sockaddr{

    sa_family_t sa_family;  // 소켓의 주소체계. PF_INET= IPv4 주소체계.

    char sa_data[14];      // 해당 주소체계에서 사용하는 주소 정보.

}

 

sockaddr_in 구조체

  IPv4 주소체계에서 사용하는 구조체다. 소켓 프로그램은 범용 주소 구조체로 sockaddr을 사용하지만, 주소체계의 종류에 따라 별도의 전용 구조체를 만들어 사용하는게 아무래도 편리할 것이다. 참고로, 기타 다른 주소체계 중 Local Unix 주소 체계는 sockaddr_un 구조체를 사용한다. 


  소켓라이브러리는 sockaddr을 사용하므로 라이브러리에 주소 정보를 넘길 때는 sockaddr로 형변환을 하여 넘긴다. 그러므로 당연히 구조체의 크기는 동일하다.  아래 세부 구조를 살펴보자.

 

struct sockaddr_in{

    sin_family_t sin_family;   // IPv4 주소체계에서 사용하므로 항상 AF_INET으로 설정

    unist16_t sin_port;         // 포트 번호

    struct in_addr sin_addr; // IP주소를 나타내는 32비트 정수 타입 구조체

    char sin_zero[8];         // sockaddr과 같은 크기를 유지하기 위해 필요한 패딩(padding) 공간. 항상 0.

}

  솔라리스에서 PF_INET과 AF_INET의 정의를 찾아보니 아래 결과가 나온다. 즉 둘은 같은 값이다.

 # grep PF_INET *.h
socket.h:#define        PF_INET         AF_INET
socket.h:#define        PF_INET6        AF_INET6
socket.h:#define        PF_INET_OFFLOAD AF_INET_OFFLOAD /* Sun private; do not use */

 

  위의 sin_zero는 항상 0이어야 하는데 이를 어기면 간혹 IP 주소를 터무니 없는 값으로 인식하는 경우가 생긴다. 그래서 일반적으로 memset (유닉스)이나 ZeroMemory (윈도우즈) 등으로 초기화한 후 사용한다.

 

  위 두 구조체(sin_addr까지 포함하면 3개)의 구조는 각 운영체제마다 사용하는 타입 이름이 다르므로 약간씩 차이가 있다.

 

소켓 페어 (socket pair)

 

  TCP/IP 프로토콜 체계에서 사용하는 주소 체계는 아래의 주소 형식을 가진다. 이것은 프로그램상의 규칙이 아니라, IP 프로토콜의 정의라는 것을 기억하자.

소켓 주소 ::= <IP 주소> + <트랜트포트 포트 번호>

  이 주소를 클라이언트 측과 서버 측의 주소로 결합해서 하나의 소켓 페어(socket pair)라고 부르며 이 소켓페어가 하나의 가상의 통신회선이 된다.

 

          서버 소켓 주소(IP:주소포트번호) *------------* 클라이언트 소켓 주소(IP:주소포트번호)

 

  만약 포트를 사용하지 않고 그냥 IP주소로만 소켓 페어를 구성한다고 생각해보자. 그러면 서버와 클라이언트 사이의 가상 회선은 오직 1개만 연결되는 셈이다. 그럴 경우 지금 내가 글을 쓰고 있는 이 네이버의 스마트 에디터 화면에서 네이버 검색을 이용하려면, 이 편집 창을 닫아야만 가능하다는 얘기다 (새로운 창으로 네이버를 접속할 수 없으므로..). 더군다나 웹브라우저는 웹서버를 통해 텍스트, 이미지, 동영상등 무수히 많은 파일을 여러 포트를 통해 동시에 다운받는데 이 회선이 모두 사라지면 그 느려 터질 속도 또한 짐작이 가지 않는가?

 

  BSD기반의 소켓 및 윈속 라이브러리는 클라이언트에서 서버로 연결할 때 connect 함수를 사용하는데 어디에도 클라이언트의 IP 주소 및 포트를 설정하는 부분이 없다. 이유는 서버 연결시 클라이언트의 IP주소와 임의로 자동 생성한 포트 번호를 사용하여 접속하기 때문이다. 그러므로 서버와 연결하는 클라이언트 프로그램을 내 컴퓨터에서 2번 이상 실행하더라도 클라이언트측 포트 번호가 무작위로 설정되어, 충돌없는 소켓 페어(가상 회선)가 자동으로 생성되므로 오류없이 실행할 수 있다.

 

  참고로 .NET에서는 소켓 주소 표현으로 EndPoint라는 클래스를 사용하는데, 클라이언트 측 포트 번호를 임의로 지정할 수 있다(0을 입력하면 라이브러리에서 중복되지 않게 자동으로 할당한다). 이 경우 클라이언트 프로그램을 2번 실행하면 어떤 일이 벌어지겠는가? '이미 연결된 포트로 바인딩할 수 없다'는 에러가  발생할 것이다. 꼭 한번 테스트해 보시라.

 

표준 예제

/* 서버용 */

struct sockaddr_in server_address;


memset(&server_address,0,sizeof(server_address));
server_address.sin_family=AF_INET;
server_address.sin_addr.s_addr=htons(INADDR_ANY);

server_address.sin_port=htons(PORT); 

 

  INADDR_ANY는 서버의 IP주소를 자동으로 찾아서 대입해주는 함수이다(복잡한 #define문으로 정의되어 있다. long형값 0). INADDR_ANY를 지정할 경우 2가지 이점이 있다.

 

    - 멀티 네트워크 카드 동시 지원

서버는 NIC을 2개 이상 가지고 있는 경우가 많은데 만일 특정 NIC의 IP주소를 sin_addr.s_addr에 지정하면 다른 NIC에서 요청된 연결은 서비스 할 수 없게 된다. 이때 INADDR_ANY를 사용하면 두 NIC을 모두 바인딩해주므로 어느 IP를 통해 접속하더라도 정상적인 서비스가 가능하다.

 

    - 이식성

또 다른 이점은 이식성인데, 특정 IP를 지정했을 경우 다른 서버 컴퓨터에 프로그램이 설치된다면 주소값을 변경(소스 수정)해야 하지만, INADDR_ANY를 사용하면 소스 수정없이 곧바로 사용 또는 컴파일할 수 있는 장점이 생긴다.

 

  서버의 주소와 포트 번호는 IP헤더에 저장되어 전송되는데 이를 중계하는 라우터들은 항상 네트워크 바이트 방식, 즉 빅 엔디언으로 처리한다. 그러므로, 소켓에서도 빅 엔디언 방식으로 정렬되어 있어야 한다. 그래서 항상 htons(host to network short) 함수로 주소와 포트번호를 변환해서 사용해야 한다.

 

/* 클라이언트용 */

struct sockaddr_in client_address;


memset(&client_address,0,sizeof(client_address));
client_address.sin_family=AF_INET;
client_address.sin_addr.s_addr= inet_addr("192.168.56.1"); // 서버 주소

client_address.sin_port= htons(PORT); 

 

  P.S) 위 예제 중 inet_addr 함수는 문자열을 받아 long형 값을 돌려주는데 Network 바이트 형식을 가진다. 예전에 아무 생각없이 htonl(inet_addr("ip"))로 사용했다가 반나절을 삽질한 적이 있다. 이런 오류는 화면상에 표시도 되지 않으므로 디버거를 통해야한 확인이 가능하다. 몇 가지 안되는 소켓관련 함수는 사용법을 꼭 주지해야 한다.


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

컨테이너 종류 복습  (0) 2013.12.21
텍스처 아틀라스  (0) 2013.12.20
맴버함수를 쓰레드로 쓰는법  (0) 2013.11.27
assert 사용법  (0) 2013.11.27
무작정 쳐보자 <CSTREAM>  (0) 2013.11.24
Posted by JJOREG

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

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
이전버튼 1 2 이전버튼