// 대부분의 설명은 리눅스를 기초로 해서 쓰여지나 windows와도 거의 차이가 없다.


일단 소켓 함수를 확실히 확인해보자.

SOCKET socket(

  _In_  int af, -> 소켓이 사용할 프로토콜 체계 

  _In_  int type, -> 소켓의 타입

  _In_  int protocol -> 프로토콜 체계중 실제적으로 상요할 프로토콜

);


프로토콜? ---------------------------------------------------------------------------------------------------------

프로토콜이란 컴퓨터 상호간에 필요한 통식규약이다. 신호를 보내는 방법부터 이런런 식으로 신호를 보낸다고 미리 약속해 놓은 규약이다.


// 프로토콜의 체계

■ 소켓 생성 시 사용되는 인자 도메인은 PF_INET과 AF_INET이 있다.
- PF_INET는 프로토콜 체계(프로토콜 패밀리)중 하나이고, AF_INET는 주소 체계(주소 패밀리)중 하나이다.

(1) PF_INET 프로토콜 체계

 프로토콜 체계(Protocol Family)  정의
 PF_INET IPv4인터넷 프로토콜
 PF_INET6 IPv6인터넷 프로토콜
 PF_LOCAL LOCAL 통신을 위한 UNIX 프로토콜
 PF_PACKET Low level socket을 위한 인터페이스
 PF_IPX IPX 노벨 프로토콜

소켓을 만들 때는 소켓이 사용될 환경을 고려해 프로토콜을 설정해 줘야 한다.
다시 말해 프로토콜 패밀리는 소켓을 생성할 때 이 소켓이 어떤 프로토콜을 사용해 통신을 할지 정한다.
참고로 소켓은 네트워크 통신을 할때만 사용되는 것이 아니다.
유닉스 계열의 시스템에서 시스템 내부의 프로세스들끼리 통신을 하기 위해서도 사용된다. 
자주 사용되는 PF_INET는 프로토콜 패밀리중 하나이다. 

(2) AF_INET 주소 체계

 주소체계(Address Family) 정의
 AF_INET  IPv4인터넷 프로토콜
 AF_INET6  IPv6인터넷 프로토콜
 AF_LOCAL  LOCAL 통신을 위한 UNIX 프로토콜

이 들은 주소 구조체 안에 주소 패밀리를 정의할 때 사용한다.
프로토콜 체계를 나타내는 PF_INET와 주소체계를 나타내는 AF_INET 는 같은 상수 값을 가지고 있다.
그렇다고 해서 주소정보를 설정하는 부분에 PF_INET를 사용하고 프로토콜 패밀리 정보를 설정하는 부분에 AF_INET를 넣는 것은 좋지 않다.

결과적으로, 프로토콜 체계를 설정하는 부분은 PF로 시작하는 상수를 사용하고, 주소 체계를 설정하는 부분은 AF로 시작하는 상수를  사용하는 것이 좋다.

실제 코딩 부분에서 socket()함수에 프로토콜 패밀리에 AF_INET를 넣어도 되지만 PF_INET를 넣는게 바람직하고,
struct sockaddr_in 구조체에 주소 체계를 넣을 때에도 PF_INET 를 넣어도 되지만 AF_INET를 넣는게 바람직하다.

소켓의 타입 ------------------------------------------------------------------------------------------------------

소켓의 타입에는 2가지가 존재한다.


1. 연결 지향형 소켓 

SOCK_STREAM(TCP 소켓을 할당할때 상요되는 인자값.)

* 소켓과 소켓이 계속 연결되어있는 상태를 유지한다.

* 연결지향형 소켓의 특징은 소켓과 소켓의 연결은 1 vs 1이다.

* 신뢰성 있는 순차적인 바이트 기반의 연결지향 데이터 전송 방식의 소켓

// 추가적으로 이해해야할 부분


2. 비 연결 지향형 소켓 

SOCK_DGRAM (UDP 소켓을 할당할때 사용되는 인자값.)

* 소켓이 특정 주소로 메세지를 보내고 다른 한 소켓은 연셜상태 관련없이 메세지가 오면 받기만 하면 된다.

* 즉 주소로 보내기만 할 뿐이지 데이터의 파손이나 분실에 대해서는 체크할 수 없다.

* 전송되는 데이터의 경계가 존재한다.

* 한번에 전송 할 수 있는 데이터의 크기가 제한 된다.

// 추가적으로 이해해야할 부분


프로토콜 최종결정 --------------------------------------------------------------------------------------------------

어떤 체계를 사용할지도 결정했고 어떤 연결 방식을 사용할지도 결정했다. 그렇다면 다음으로 진짜 사용할 프로토콜의 종류를 결정해야할 때다. 간단한 몇가지 인자값을 보도록하자.


* 윈도우 환경에서 C언어의 ENUM 값으로 지정된 인자값이라는 것을 유념하고 본다.

    IPPROTO_HOPOPTS       = 0,  // IPv6 Hop-by-Hop options 자동으로 앞의 인자값을 기준으로 결정해주는것 같다..

    IPPROTO_ICMP          = 1,

    IPPROTO_IGMP          = 2,

    IPPROTO_GGP           = 3,

    IPPROTO_IPV4          = 4,

    IPPROTO_ST            = 5,

    IPPROTO_TCP           = 6, -> TCP가 보인다.

    IPPROTO_CBT           = 7,

    IPPROTO_EGP           = 8,

    IPPROTO_IGP           = 9,

    IPPROTO_PUP           = 12,

    IPPROTO_UDP           = 17, -> UDP 요있네.

    IPPROTO_IDP           = 22,

    IPPROTO_RDP           = 27,

    IPPROTO_IPV6          = 41, // IPv6 header

    IPPROTO_ROUTING       = 43, // IPv6 Routing header

    IPPROTO_FRAGMENT      = 44, // IPv6 fragmentation header

    IPPROTO_ESP           = 50, // encapsulating security payload

    IPPROTO_AH            = 51, // authentication header

    IPPROTO_ICMPV6        = 58, // ICMPv6

    IPPROTO_NONE          = 59, // IPv6 no next header

    IPPROTO_DSTOPTS       = 60, // IPv6 Destination options

    IPPROTO_ND            = 77,

    IPPROTO_ICLFXBM       = 78,

    IPPROTO_PIM           = 103,

    IPPROTO_PGM           = 113,

    IPPROTO_L2TP          = 115,

    IPPROTO_SCTP          = 132,

    IPPROTO_RAW           = 255,

    IPPROTO_MAX           = 256,

    IPPROTO_RESERVED_RAW  = 257,

    IPPROTO_RESERVED_IPSEC  = 258,

    IPPROTO_RESERVED_IPSECOFFLOAD  = 259,

    IPPROTO_RESERVED_MAX  = 260


함수를 채워보자 --------------------------------------------------------------------------------------------------


SOCKET socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

IPv4 인터넷 프로토콜 체계에서 동작하는 연결지향형 TCP 소켓을 생성하여 반환하라.


SOCKET socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

IPv4 인터넷 프로토콜 체계에서 동작하는 비 연결지향형 UDP 소켓을 생성하여 반환하라.




소켓에 할당되는 IP주소 ---------------------------------------------------------------------------------------------


1. IP주소 : 라우터(정확히 말하면 물리적인 회선이라고 이해하는 편이 빠를듯 하다. 컴퓨터에 꼽는 랜, 혹은 허브 등에)에 부여되는 주소.

2. 프로토콜 주소 : 프로그램상에서 생성되는 소켓을 구분하기 위해 소켓에 부여되는 번호.


IP주소의 체계

* IPV4( Internet Protocol version 4 ) 4바이트 주소 체계 (우리가 일반적으로 알고 있는 255.162.42.182 같은 형식)

* IPV4( Internet Protocol version 6 ) 16바이트 주소 체계


라우터와 스위치 --------------------------------------------------------------------------------------------------

네트워크를 구성하려면 물리적인 장치가 필요하다. 여기서 물리적인 장치란 LEN카드 같은 것을 생각해 보면 편하다. 선이 연결되어 있거나 무선으로 전파를 송수신 할 수 있는 물리적인 무언가가 있어야만 우리는 네트워크를 이용할 수가 있다.

IP주소란 그런 라우터나 혹은 스위치에 할당되는 주소라고 이해한다면 편 할 것이다.


PORT번호 ------------------------------------------------------------------------------------------------------


IP주소로 내 컴퓨터의 주소는 부여됐다 PORT번호는 이렇게 생각해보자.

RPG게임 하나와 FPS게임 하나를 동시에 켰다. 둘은 모두 네트워크 게임이기 때문에 양쪽을 하나의 컴퓨터에게 동시에 켜고 동시에 접속했다.

이 녀석들은 하나의 소켓으로 한번에 데이터를 다 받을까? 아니다.

RPG는 RPG의 소켓을 가지고 FPS는 FPS의 소켓을 가진다.

물론 IP주소로는 모든 데이터가 하나가 되어 컴퓨터에 전송된다. 하지만 전송된 데이터는 RPG와 FPS의 데이터 정보를 모두 가지고 있으므로 적절한 분배가 필요하다.

****(여기까지는 하드웨어적인 신호라고 생각하자.)


컴퓨터에는 NIC(네트워크 인터페이스 카드)라고 불리는 데이터 송수신 장치가 하나씩 달려있는데.

이 녀석의 역할은 물리적인 장치로 송신된 데이터를 컴퓨터의 내부의 데이터로 직접 전송시켜 주게 된다. 

****(여기까지는 운영체제에 데이터를 넘긴다고 생각하자.)


이때 이 데이터안에는 PORT번호가 새겨져 있고, 데이터를 전송받은 컴퓨터의 내부의 운영체제에서 PORT번호를 이용하여 데이터를 분배하여 RPG와 FPS의 네트워크 데이터를 전달해 주게 된다.

****(운영체제가 각각 RPG와 FPS에 데이터를 분배한다.)


주소정보의 표현 ---------------------------------------------------------------------------------------------------

종합적으로보면 하나의 소켓통신을 하기위한 주소는 다음의 정보를 가져야 한다.

1. 주소체계

2. IP주소

3. PORT주소


소켓통시네서 각기 따로 입력되는 것이 아니라. 다음의 구조체를 통해서 하나의 정보로서 묶여있다.


struct sockaddr_in

{

sa_family_t           sin_family;     //주소체계

uint16_t                sin_port;       //16비트 PORT주소

struct in_addr       sin_addr       //32비트 IP주소

char                    sin_szro[8]  //사용되지 않는 여분의 공간

}

// ip주소는 구조체로 전체 주소를 위한 구조체 안에 포함되어있다.

struct in_addr

{

in_addr_t        s_addr;        //32비트 IPv4 인터넷 주소

}


하지만 이 주소정보 구조체는 본래 이런 모양이 아니라 다음과 같은 구조체로 정의되어 있었다.

struct sockaddr

{

sa_family_t           sin_family;     // 주소체계

char                    sa_data[14]; // 주소 정보

}


2가지 구조체가 있는 이유는 의외로 단순하다. 주소는 모든 정보를 요구하고 마지막에는 8바이트의 0으로 채워진 공간을 요구하는데.

sockaddr구조체에 그런 정보를 넣기가 매우 불편하기 때문에 sockaddr_in 구조체로 좀더 손쉽게 주소를 넣게 해준 것이다.


리틀에디안 빅 에디안 -----------------------------------------------------------------------------------------------

네트워크주소는 CPU에 바이트 순서로 저장되는데 CPU마다 바이트순서를 저장하는 방식이 달라. 애써 입력한 주소가 제대로 기능하지 못하는 경우가 있다. 설명하자면 아래 그림과 같다.





그래서 통신규약으로는 보내지는 정보는 모두 빅에디안으로 보내도록 되어있다.

하지만 리틀에디안으로 정보를 처리하는 CPU에는 빅에디안 정보는 제대로 전송되지 않기 때문에 다음과 같은 함수를 통해서 변환 작업을 거친다.


htons(short s) -> short데이터를 호스트의 바이트 순서에서 네트워크 바이트 순서로 변환해라.

ntohs(short s) -> short데이터를 네트워크 바이트 순서에서 호스트 바이트 순서로 변환해라.


htonl(long s) -> long데이터를 호스트의 바이트 순서에서 네트워크 바이트 순서로 변환해라.

ntohl(long s) -> long데이터를 네트워크 바이트 순서에서 호스트 바이트 순서로 변환해라.


자신의 CPU가 리틀에디안이건 빅에디안이건 어느 컴퓨터에서나 돌아갈 수 있도록 이변환을 이용해 주는 것이 좋다.


인터넷 주소의 초기화와 할당 -----------------------------------------------------------------------------------------

네트워크 바이트 순서의 정수로 변환하는 역할.
sockaddr_in안에서 주소정보를 저장하기 위한 멤버는 32비트 정수형으로 정의되어 있다. 하지만 실제 구조체를 받아들일때는 
주소 정보 같은 문자열 주소를 사용하며 당연히 이를 변환해주기 위한 변수도 존재한다.

inet_addr ------------------------
inet_addr(const char* string );
성공시 빅에디안으로 변환된 32비트 정수 값, 실패시 INADDR_NONE 반환.

이 함수를 제외하고 다음과 같은 함수도 존재하지만 인자 값으로 in_addr을 하나 더 받는다.
inet_aton ------------------------
inet_aton(const char* string in_addr);
성공시 (true) 실패시 (false 반환)

반대로 구조체 안에 있는 정보를 다시 문자열 형으로 변환해주는 기능을 하는 함수도 존재한다.
inet_ntoa ------------------------
inet_ntoa(struct in_addr adr
성공시 변환된 문자열의 주소값 실패시 -1 반환

인터넷 주소의 초기화의 실제 코드 ------------------------------------------------------------------------------------
그럼 함수들을 이용하여 주소값을 초기화하는 코드를 작성해본다.

struct socketaddr_in addr;
char *serv_ip="211.217.168.13";
char *serv_port="9190";
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(serv_ip);
addr.sin_port = htons(atoi(serv_port));

소켓에 인터넷 주고 할당하기 ------------------------------------------------------------------------------------
소켓에 인터넷 주소를 할당하기 위해서는 다음과 같은 함수가 필요하다.
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
int sockfd -> 주소 정보를 (IP와 PORT를) 할당할 소켓의 파일 디스크럽터.
struct sockaddr *myaddr 할당하고자 하는 주소정보를 지니는 구조체 변수의 주소값.
socklen_t addrlen 두번째 인자로 전달된 구조체 변수의 길이정보


Posted by JJOREG