1. 소켓의 개념과 생성, 프로토콜의 설정

MFC 소켓 클래스에 들어가기 전에 윈속 소켓 프로그래밍에 대해 간략하게 정리 해보겠습니다.

1.1 소켓의 개념
   네트워크를 이용한 통신은 전화망을 이용한 통신과 그다지 다르지 않습니다. 전화망을 이용한 통신에서 전화기는 사람과 사람을 이어주는 역할을 하는데요. 이를 일종의 소켓이라고  할 수 있겠습니다.
  단도직입적으로 말하면, 소켓이란 떨어져 있는 두 개체를 연결해 주는 일종의 도구라고 할 수 있겠네요.
1.2 소켓 구현
1.2.1 윈속(Winsock) 초기화 하기
  윈속 프로그래밍시에는 반드시 윈속 초기화를 해야 합니다. 이는 프로그램에서 사용하는 윈속버전을 표시함으로써 이 버전의 윈속 사용을 위한 라이브러리 초기화 작업을 위함 입니다.

#include <winsock2.h>

int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);

  
  첫번째 파라미터에는 프로그램에서 사용하는 윈속의 버전을 WORD 형으로 변환한 값이 들어갑니다. 상위 8비트에는 부버전을 하위 8비트에는 주버전을 표시합니다. 예를 들어 2.2 버전을 사용한다면 이 파라미터에는 0x0202를 사용하고 3.2 버전을 사용한다면 0x0203을 사용하게 되는 것 입니다. 이를 편리하게 변환하기 위해 MAKEWORD 함수(매크로 함수)를 사용하는 것이 일반적이에요.

  두번째 파라미터에는 WSADATA 타입 변수의 포인터가 들어갑니다. 함수 호출이 성공적으로 이루어지면 이 변수에는 로딩한 DLL의 정보가 채워지게 됩니다.

프로그램 종료시에는 WSADATA를 해제해 주어야 하는데요. 이때 사용하는 함수가 WSACleanup 함수 입니다.

int WSACleanup(void); // 성공시 0, 실패시 SOCKET_ERROR 리턴

1.2.2 소켓 생성
 소켓 생성이란 전화망을 이용한 통신에 비유해 볼때 전화기를 마련하는 것과 같습니다.

SOCKET socket(int af, int type, int protocol); // 성공시 소켓 핸들, 실패시 INVALID_SOCKET 리턴

  첫번째 파라미터에는 주소의 계열을 표시합니다. 
       PF_INET : IP v4의 주소 체계
       PF_INET6 : IP v6의 주소 체계
       PF_LOCAL : Local 통신을 위한 유닉스 프로토콜
       PF_PACKET : Low level socket을 위한 인터페이스
       PF_IPX : IPX 노벨 프로토콜

  두번째 파라미터에는 소켓의 타입을 지정합니다.
       SOCK_STREAM : TCP프로토콜을 사용시
       SOCK_DGRAM :  UDP프로토콜을 사용시

  세번째 파라미터에는 이 소켓에서 데이터를 전송할때 사용할 프로토콜을 지정합니다.

       IPROTO_TCP : TCP 프로토콜 사용시
       IPROTO_UDP : UDP 프로토콜 사용시
  하지만 실제 프로그래밍시에는 보통 0을 써주는데, 0을 넣게 되면 주소체계와 타입을 보고 자동으로 설정하게 됩니다.

1.2.3 주소와 포트 할당 
  전화기만 있고 전화번호가 없다면 우리는 통화를 할 수 없겠죠. 전화기에 전화번호를 할당해주는 역할을 하는 함수가 바로 bind 함수 입니다.

int bind(SOCKET s, const struct sockaddr FAR *name, int namelen); // 성공시 0, 실패시 SOCKET_ERROR 리턴

  첫번째 파라미터에는 사용할 소켓의 객체가 들어갑니다.

  두번째 파라미터에는 서버의 주소가 들어가는데 이에 대해서는 다음 포스팅에 자세히 설명하도록 하겠습니다.

  세번째 파라미터에는 서버 주소의 길이가 들어갑니다.

1.2.4 연결 요청 대기(listen) 상태로 진입
  이제 서버에서는 클라이언트가 연결을 요청할 수 있는 상태로 만들어 줘야 하겠죠? 이 역할을 하는 함수가 listen 함수 입니다.

int listen(SOCKET s, int backlog); // 성공시 0, 실패시 SOCKET_ERROR 리턴

  첫번째 파라미터에는 사용할 소켓의 객체가 들어갑니다.

  두번째 파라미터에는 버퍼의 크기가 들어갑니다. 서버에 연결 신호가 동시에 많이 왔을 때는 특정 버퍼에 저장을 해두고 연결을 해 줘야 하는 경우가 생기게 되는데 소켓에 그 버퍼의 크기를 지정하는 부분이 되겠습니다.
1.2.5 연결 수락
  클라이언트가 서버에 연결 요청을 하면 이를 수락해 주는 부분입니다. 이 역할은 담당하는 함수가 바로 accept 함수 입니다.

SOCKET accept(SOCKET s,  struct sockaddr FAR *addr, int FAR *addrlen); // 성공시 소켓 핸들, 실패시 INVALID_SOCKET 리턴

  파라미터의 형태는 bind 함수와 동일하나 bind 함수는 서버의 주소를 사용하고 accept 함수는 클라이언트의 주소를 사용합니다.

1.2.6 클라이언트에서의 연결 요청
  클라이언트에서 서버로 연결 요청을 할 때는 connect 함수를 사용합니다.

int connect(SOCKET s, const struct sockaddr FAR *name, int namelen); // 성공시 0, 실패시 SOCKET_ERROR 리턴



1. sockaddr_in 구조체
  소켓을 사용하기 위해선 소켓에 서버의 IP와 Port를 바인딩 해주어야 합니다. 이 과정에서 IP와 Port를 저장하는 sockaddr_in 구조체를 보셨을 겁니다. sockaddr_in 구조체는 IPv4의 주소 체계를 저장할 수 있는 구조체 입니다.

struct sockaddr_in {
    sa_family_t       sin_family;    
   
// 주소의 계열 (이전 포스팅에서 나왔던 socket 생성 함수의 첫 인자 값과 동일)
    uint16_t             sin_port;        
   
// 16bit TCP / UDP Port
    struct in_addr    sin_addr;        
    
// 32bit IPv4 Address
    char                 sin_zero[9];    
    
// 사용하지 않습니다.
};
           - sockaddr_in 구조체의 원형-

struct in_addr(
    uint32_t        s_addr;
};
           - in_addr 구조체의 원형 -
2. Byte Order 변환
  네트워크 상에서는 Big-Endian 방식으로 데이터를 전송한다. sockaddr_in 구조체에 저장되는 값들은 모두 Big-   Endian 방식으로 저장되어야 합니다. byte order 변환 함수에는 다음과 같은 것이 있습니다.

- htons : host byte order를 short 형의 network byte order로 변환
- ntohs : network byte order를 short 형의 host byte order로 변환
- htonl : host byte order를 long 형의 network byte order로 변환
- ntohl : network byte order를 long 형의 host byte order로 변환

 

3. 주소 문자열을 Big-Endian 32bit 값으로 변환
주소 문자열을 Big-Endian 32bit 값으로 변환하기 위해서 inet_addr() 함수를 사용합니다.

unsigned long inet_addr(const char *string);
                 - inet_addr 함수의 원형 -

+ Recent posts