네트워크 프로그래밍03 주소체계와 데이터 정렬

zh025700·2022년 3월 30일
0

네트워크 프로그래밍


3. 주소체계와 데이터 정렬

소켓을 생성하고 소켓에 주소정보를 넣어줘야한다.
이때 주소정보는 IP와 PORT 번호이다.
IP는 Internet Protocol의 약자로 인터넷 상에서 데이터를 송수신할 목적으로 컴퓨터에 부여하는 값이다
PORT 번호는 컴퓨터가 아닌, 프로그램 상에서 생성되는 프로세스 /소켓을 구분하기 위해 부여되는 번호다.

인터넷 주소 (IP)

인터넷에서 **정보를 주고 받기** 위해서는 IP번호가 부여되야 한다.
IP 주소체계는 두 종류로 나뉜다.
  • IPv4( 4바이트)
  • IPv6( 16바이트)

PORT 번호

IP는 컴퓨터를 구분하기 위해 존재한다.
그래서 IP를 이용해 컴퓨터로 데이터를 전송은 가능하다.
하지만! 컴퓨터에서 어떤 소켓,프로세스로 데이터를 전달하는가??
이를 port번호로 구분한다!

하나의 운영체제에서 소켓을 구분하는 목적으로 사용된다!

IP주소로 통신할 컴퓨터를 찾고 PORT 번호를 사용해 해당 컴퓨터의 어떤 프로세스(프로세스의 소켓)에 데이터를 전달할지 찾는다!!!!

주소 정보의 표현

주소체계, IP정보, Port 번호 모두를 담아두는 구조체가 존재한다
이 구조체는 bind함수에 주소 정보를 전달하는 용도로 사용된다.
struct sockaddr_in{
    u_char sin_len // 구조체 길이
sa_family_t    sin_family //주소체계 AF_INET
uint16_t    sin_port // 16비트 TCP/UDP 포트번호
struct in_addr    sin_addr // 32비트 IP 주소
char sin_zero[8] // 사용안함
}

그리고 in_addr 구조체는

struct in_addr{
    in_addr_t s_addr // 32비트 IPv4 인터넷 주소(빅 엔디안)
}

로 되어있따.

변수명 앞 자료형은 확장성을 위해 정의한 자료형들이다.
크게 알 필요가 현재는 없어 보여 정리 안함.

네트워크 바이트 순서

cpu가 데이터를 메모리에 저장하는 방식은 두가지로 나뉨.
=>CPU가 데이터를 해석하는 방식도 두가지로 나뉨

00 12

  • 빅엔디안

    • 상위바이트의 값을 작은 번지수에 저장
    • 00 12
  • 리틀 엔디안

    • 상위 바이트의 값을 큰 번지수에 저장

    • 12 00

      IP주소와 PORT 번호는 정수형이다

다른 저장 방식을 사용하는 시스템끼리의 통신에서 오류가 일어난다!!

해결법

  • 호스트 바이트 순서
    • 호스트가 사용하는 바이트 순서(빅엔디안 or 리틀엔디안)
  • 네트워크 바이트 순서
    • 네트워크에서 사용되는 바이트 순서 항상 빅엔디안

그래서 네트워크에선 빅 엔디안으로 통일했다
리틀엔디안 시스템의 통신에선 빅 엔디안으로 데이터를 재 정렬해야한다!

바이트 순서 변환

u_short htons(u_short x);
u_long htonl(u_long x);
u_long ntohl(u_long x);
u_short ntohs(u_short x);

h는 host의 바이트 순서를 의미
n은 네트워크의 바이트 순서를 의미
s는 short
l은 long

htons는 short형을 호스트 바이트 순서에서 네트워크 바이트 순서로 변환
ntohs short형을 네트워크 바이트 순서에서 호스트 바이트 순서로 변환

s는 port 번호 변환에 사용되고 l은 IP주소의 변환에 사용됨
s = 2바이트(16비트) l은 4바이트(32비트)이기 때문
port = 16비트, ip= 32비트

빅엔디안 기기에선 이 작업이 의미 X
리틀엔디안에선 바이트 순서를 뒤 바꿈!

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

IP주소에는 점이 찍혀있다.  127.0.0.1
이렇게 문자열로 표현된 IP주소를 32비트 정수형으로 변환해주는 함수가 있다.
in_addr_t inet_addr(char* string)
int inet_aton(char*string,struct in_addr* addr)
inet_aton을 실제 더 많이 사용하는데 이유는 aton은 소켓 구조체에다 변환된 ip주소를 할당해주기 때문이다.

그럼 위와 반대되는,
32비트 정수형 IP주소를 문자열로 바꿔주는 함수도 있다.
char* inet_ntoa(struct in_addr adr);

반환형이 포인터다!! 함수 내부적으로 메모리 공간을 할당해서 변환된 문자열 정보를 저장한다. 그래서 이 함수를 호출하면 전에 저장된 문자열 정보가 지워질 수 있으니 별도에 메모리 공간에 반환값을 미리 복사를 하는것이 좋다!

인터넷 주소의 초기화

위의 내용을 바탕으로 소켓을 생성할때 초기화 방법이다.
struct sockaddr_in addr;            // 구조체 선언
char* serv_ip = "127.0.0.1"         // ip주소 문자열
char* serv_port = "1";              // port 번호 문자열
memset(&addr,0,sizeof(addr));       // 구조체를 0으로 초기화 sin_zero때문에
addr.sin_family=AF_INET;            // 주소체계 지정
addr.sin_addr.s_addr = inet_addr(serv_ip);  // IP주소 초기화
addr.sin_port = htons(atoi(serv_port));     // 포트번호 초기화

위의 과정을 거쳐 소켓을 생성한다.
주로 서버에 이렇게 초기화를 한다.
서버에서는 IP 주소를 직접 할당하지 않고
INADDR_ANY를 사용해도 된다.

INADDR_ANY

addr.sin_addr.s_addr=htonl(INADDR_ANY);

이를 사용하면 자동으로 서버의 IP주소가 할당된다.

클라이언트의 주소정보 초기화

서버는 소켓에 IP와 포트번호를 할당해 이 주소로 오는 데이터를 받는다.
반면 클라이언트는 IP와 포트번호를 할당해 그 주소로 데이터를 보낸다

서버에서는 소켓 구조체를 하나 선언해 서버가 동작하는 컴퓨터의 IP와 소켓에 부여할 PORT번호를 초기화한 후 bind를 호출한다

반면 클라이언트는 구조체를 선언해 연결할 서버 소켓의 IP와 PORT번호로 초기화한다!!

profile
정리

0개의 댓글