[TCP/IP Socket]Chapter 03 - 주소체계와 데이터 정렬

Lee Jeong Min·2021년 1월 27일
1

네트워크

목록 보기
3/17
post-thumbnail

03-1 소켓에 할당되는 IP주소와 PORT번호

인터넷 주소(Internet Address)

IP주소 체계는 다음과 같이 두 종류로 나뉜다.

  • IPv4(Internet Protocol version 4) - 4바이트 주소체계
  • IPv6(Internet Protocol version 6) - 16바이트 주소체계

IPv4주소의 형태에 따라서 A,B,C,D,E 이렇게 총 5가지의 클레스로 분류가 됨.

네트워크 주소란 네트워크의 구분을 위한 IP주소의 일부를 말한다. 총 4바이트의 주소가 있을 때, 처음부터 4바이트 IP주소를 참조하여 그 컴퓨터로 데이터가 전송되는 것이 아닌 4바이트 IP주소 중에 네트워크 주소만 참조하여 네트워크로 데이터가 전송됨.
203.211.172.101 이라는 IP주소가 있다고 가정하고 203.211.172가 네트워크 주소라고 가정하면 이 3바이트 주소로 먼저 데이터를 전송한다는 의미.
이 이후에 로컬 네트워크의 라우터 또는 스위치를 통해 데이터가 전송이 됨.

클래스 별 네트워크 주소와 호스트 주소의 경계

  • 클래스 A의 첫 번째 바이트 범위: 0이상 127이하
  • 클래스 B의 첫 번째 바이트 범위: 128이상 191이하
  • 클래스 C의 첫 번째 바이트 범위: 192이상 223이하

--> 이러한 기준이 정해져 있어서 소켓을 통해서 데이터를 송수신할 때, 별도로 신경 쓰지 안항도 네트워크로 데이터가 이동하고 이어서 최종 목적지인 호스트로 데이터가 전송이됨.

소켓의 구분에 활용되는 PORT번호

컴퓨터에는 NIC이라 불리는 데이터 송수신장치가 하나씩 달려 있고, IP는 데이터를 NIC를 통해 컴퓨터 내부로 전송하는데 사용된다. 그러나 컴퓨터 내부로 전송된 데이터를 소켓에 적절히 분배하는 작업은 운영체제가 담당하고, 이때 운영체제는 PORT번호를 활용함.

포트번호에서 0~1023은 잘 알려진(Well-known PORT)라고 해서 특정 프로그램에 할당하기로 예약이 되어있어서 이 범위의 값을 제외한 다른 값을 할당해야함. TCP와 UDP소켓의 포트번호는 중복되어도 괜찮다.


03-2 주소정보의 표현

IPv4 기반의 주소표현을 위한 구조체

struct sockaddr_in
{
    sa_family_t        sin_family; // 주소체계(Address Family)
    uint16_t           sin_port; // 16비트 TCP/UDP PORT번호
    struct in_addr     sin_addr; // 32비트 IP주소
    char               sin_zero[8]; // 사용되지 않음
};


struct in_addr
{
    in_addr_t          s_addr; // 32비트 IPv4인터넷 주소
};

위의 자료형 in_addr과 같이 별도로 정의해둔 이유는 확장성을 고려하여 32비트가 64비트로 변하여도 4바이트임을 보장받기 위해서 따로 정의를 해두었다고 한다.

구조체 sockaddr_in의 멤버에 대한 분석

  • 멤버 sin_family
주소체계(Address Family)의미
AF_INETIPv4 인터넷 프로토콜에 적용하는 주소체계
AF_INET6IPv6 인터넷 프로토콜에 적용하는 주소체계
AF_LOCAL로컬 통신을 위한 유닉스 프로토콜의 주소체계
  • 멤버 sin_port
    16비트 PORT번호를 저장한다. --> 네트워크 바이트 순서로 저장해야함.

  • 멤버 sin_addr
    32비트 IP주소정보를 저장한다. 이 또한 네트워크 바이트 순서로 저장해야한다.

  • 멤버 sin_zero
    특별한 의미를 지니지 않는 멤버이며 반드시 0으로 채워야함.

원래 이러한 정보를 다음과 같은 구조체

strcut sockaddr
{
    sa_family_t    sin_family; // 주소체계(Address Family)
    char            sa_datap[14]; // 주소 정보
}

여기에 담아서 전달을 해야하지만 너무 불편하기 때문에 sockaddr_in 에 담아서 전달하면 알아서 bind함수가 요구하는 바이트 배열이 된다.


03-3 네트워크 바이트 순서와 인터넷 주소 변환

바이트 순서와 네트워크 바이트 순서

  • 빅엔디안: 상위 바이트의 값을 작은 번지수에 저장하는 방식
  • 리틀엔디안: 상위 바이트의 값을 큰 번지수에 저장하는 방식

네트워크 바이트 순서: 빅엔디안

--> 따라서 네트워크 상으로 데이터를 전송할 때에는 데잍의 배열을 빅 엔디안 기준으로 변경해서 보내야함.

바이트 순서의 변환(Endian Conversions)

  • unsigned short htons;

  • unsigned short ntohs;

  • unsigned long htonl;

  • unsigned long ntohl;

  • htons에서 h는 호스트(host) 바이트 순서를 의미한다.

  • htons에서 n은 네트워크(network) 바이트 순서를 의미한다.

endian_conv.c

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char* argv[])
{
    unsigned short host_port = 0x1234;
    unsigned short net_port;
    unsigned long host_addr = 0x12345678;
    unsigned long net_addr;

    net_port = htons(host_port);
    net_addr = htonl(host_addr);

    printf("Host ordered port: %#x \n", host_port);
    printf("Network ordered port: %#x \n", net_port);
    printf("Host ordered address: %#lx \n", host_addr);
    printf("Network ordered address: %#lx \n", net_addr);
    return 0;
}

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

문자열 정보를 네트워크 바이트 순서의 정수로 변환하기

#include <arpa/inet.h>

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

inet_addr.c

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char* argv[])
{
    char *addr1 = "1.2.3.4";
    char *addr2 = "1.2.3.256";

    unsigned long conv_addr = inet_addr(addr1);
    if(conv_addr==INADDR_NONE)
        printf("Error occured! \n");
    else
        printf("Network ordered integer addr: %#lx \n", conv_addr);

    conv_addr = inet_addr(addr2);
    if(conv_addr==INADDR_NONE)
        printf("Error occured! \n");
    else
        printf("Network ordered integer addr: %#lx \n", conv_addr);
    return 0;
    
}

위의 add1, add2의 형식을 정수로 변환해서 출력을 해주는 것을 확인할 수 있다.

#include <arpa/inet.h>

inet inet_aton(const char * string, struct in_addr * addr);
-> 성공 시 1, 실패시 0 반환
string : 변환할 IP 주소 정보를 담고 있는 문자열의 주소 값 전달.
addr : 변환된 정보를 저장할 in_addr 구조체 변수의 주소 값 전달.

inet_aton.c

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void error_handling(char *message);

int main(int argc, char* argv[])
{
    char *addr= "127.232.124.79";
    struct sockaddr_in addr_inet;

    if(!inet_aton(addr, &addr_inet.sin_addr))
        error_handling("Conversion error!");
    else
        printf("Network ordered integer addr: %#x \n", addr_inet.sin_addr.s_addr);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

일반적으로 위에나온 inet_addr 보다 inet_aton을 주로 사용함.

인터넷 주소의 초기화

struct sockaddr_in addr;
char serv_ip = "211.217.168.13"; // IP주소 문자열 선언
char
serv_port = "9190"; // PORT번호 문자열 선언
memset(&dadr, 0 ,sizeof(addr)); // 구조체 변수 addr의 모든 멤버 0으로 초기화
addr.sin_family = AF_INET; // 주소 체계 지정
addr.sin_addr.s_Addr = inet_addr(serv_ip); // 문자열 기반 ip주소 초기화
addr.sin_port = htons(atoi(serv_port)); // 문자열 기반 PORT번호 초기화

INADDR_ANY

일일히 IP주소를 입력하지 않아도 INADDR_ANY라는 이름의 상수를 통해 컴퓨터의 IP주소가 자동으로 할당이 되게 만들 수 있다.


03-5 윈도우 기반으로 구현하기

이 부분은 생략 깃헙 코드 참조
https://github.com/hustle-dev/SocketProg/tree/master/Socketprog
endian_conv_win.c
inet_adrconv_win.c
conv_addr_win.c

profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글