주소체계와 데이터 정렬

interviewsangu·2020년 7월 26일
0

소켓 프로그래밍

목록 보기
4/8
post-thumbnail

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

인터넷 주소

  • IPv4 : 4바이트 주소체계
  • IPv6 : 16바이트 주소체계

IPv4 기준을 4바이트 IP 주소는 네트워크 주소와 호스트 주소로 나뉘며, 주소의 형태에 따라서 클래스가 분류된다. 네트워크 주소를 통해 라우터를 찾고 나머지 호스트 주소를 이용해 호스트에 접근한다.

라우터는 외부와 호스트의 데이터 전송을 목적으로 하는 일종의 컴퓨터이다.

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

클래스 A의 첫 번째 바이트 범위 0~127
클래스 B의 첫 번째 바이트 범위 128~191
클래스 C의 첫 번째 바이트 범위 192~223, 과 같이 클래스 별 IP 주소의 경계를 나누어 놓았다.
이는 첫 번째 바이트만 보면 네트워크 주소가 몇 바이트인지 판단이 가능하도록 한다.
A의 경우 첫 비트가 항상 0으로, B의 경우 10, C의 경우 110으로 시작한다.

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

  • 최종 목적지는 응용프로그램이기 때문에, 포트가 필요하다.
    일반적으로 같은 포트 번호 2개 이상을 할당할 수 없지만, 소켓의 타입이 다르면 가능하다.
    예) TCP 2020, UDP 2020
  • 포트번호는 2바이트, 즉 16비트로 표현되므로 0~65535까지 표현이 가능하며, 0~1023 까지는 Well-known PORT로 특정 프로그램에 미리 할당이 되어있는 상태이다.
  • 컴퓨터에는 NIC(Network Interface Card)가 존재하며, IP는 데이터를 NIC를 통해 컴퓨터 내부로 전송하는데 사용된다. 내부로 전송된 데이터를 소켓에 할당하는 작업은 운영체제가 담당한다.

주소정보의 표현

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

주소정보를 위해서 세가지가 필요하다 : 주소체계, IP주소, 포트번호
다음의 구조체는 bind 함수에 주소 정보를 전달하는 용도로 사용된다.
struct sockaddr_in
{
sa_family_t sin_family // 주소체계(Address Family)
uint16_t sin_port; // 16비트 포트번호
struct in_addr sin_addr; // 32비트 IP주소
char sin_zero[8] // 형 변환을 위해 필요
};
struct in_addr
{
in_addr_t s_addr; // 32비트 IPv4인터넷 주소
};

sockaddr_in 멤버

  • sin_family : IPv4, IPv6인지 주소체계를 구분해야 한다. (AF_INET, AF_INET6)
  • sin_port : 16비트 PORT 번호를 저장한다. 단, 네트워크 바이트 순서로(Big endian)
  • sin_addr : 32비트 IP 주소를 네트워크 바이트 순서로 저장한다.
  • sin_zero : 다음의 기존 구조체를 보자,
    struct sockaddr{
    sa_family_t sin_family;
    char sa_data[14]; // 주소 정보
    };
    원래는 위의 구조체를 주소 정보를 저장하는데 사용했으나, IPv4에 맞게 정의를 다시 한 구조체가 sockaddr_in에 해당한다. 주소 정보는 포트 2바이트, IP 4바이트에 해당하고 sin_zero 8바이트를 합치면 총 14바이트가 된다.
    따라서 형 변환시, 구조체의 크기가 들어맞을 수 있게된다.
    struct sockaddr_in serv_addr;
    bind(serv_sock, (struct sockaddr*) &serv_addr.... 형변환 후 주소를 전달한다.

네트워크 바이트 순서와 주소 변환

바이트 순서(order)와 네트워크 바이트 순서

  • Big Endian : 상위 바이트 값을 작은 번지수에 저장하는 방식
  • Little Endian : 상위 바이트 값을 큰 번지수에 저장(대부분의 AMD)

정수 0x1234를 저장하는 방식을 살펴보자, Big Endian의 경우 0x34(1번지) 0x12(2번지)
Little Endian의 경우 0x12(1번지) 0x34(2번지)에 저장된다.
Little Endian의 경우 뒤에서부터 떼서 스택에 쌓는다고 생각하고, Big의 경우 앞에서부터 떼서 스택에 쌓는다고 생각하면된다.

네트워크 바이트 순서는 Big Endian으로 통일되어있고, 호스트 바이트 순서가 이에 맞추면 된다. 이는 소켓이 알아서 진행 해준다.

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

  • unsigned short htons(unsigned short)
    호스트에서 네트워크로 short 데이터 형을 변환한다.(port)
  • unsigned short ntohs(unsigned short)
  • unsigned long htonl(unsigned long);
  • unsigned logn ntohl(unsigned long);
    s는 2바이트, l은 4바이트 이므로 각각 포트와 IP주소를 나타낸다.

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;
}

0개의 댓글