Network Byte Order (네트워크 바이트 순서)
1. 네트워크 통신은 바이트 단위로 이루어진다.
2. 네트워크 통신은 빅 엔디안 방식을 사용한다.
3. n 은 Network을 뜻하며, 바이트 단위, 빅 엔디안 방식을 의미한다.
4. a 는 Address를 뜻하며, 문자열 단위, 리틀 엔디안 방식을 의미한다.
5. h 는 Host를 뜻하며, 인텔 그리고 AMD 계열의 CPU를 사용하는 사용자의 PC를 의미한다.
빅 엔디안(Big Endian)
상위 바이트의 값을 작은 번지수에 저장하는 방식
리틀 엔디안(Little Endian)
상위 바이트의 값을 큰 번지수에 저장하는 방식
네트워크 바이트 순서(빅 엔디안 시스템)에서 0x12 , 0x34 의 조합으로 만들어지는 값은, 리틀 엔디안 시스템에서 0x34 , 0x12의 조합으로 만들어지는 값과 같다. 즉, 전송되어온 데이터의 해석 순서가 바뀌어야 동일한 값으로 인식된다.
Endian Conversions (바이트 순서의 변환)
unsigned short htons(unsigned short); // host-to-network 바이트 변환
unsigned short ntohs(unsigned short); // network-to-host 바이트 변환
unsigned long htonl(unsigned long); // host-to-network 바이트 변환
unsigned long ntohl(unsigned long); // network-to-host 바이트 변환
// h는 호스트 (host) 바이트 순서
// n은 네트워크 (network) 바이트 순서
// S는 short
// l은 long
// 뒤에 s가 붙는 함수는 s가 2 바이트 short를 의미하므로 PORT번호의 변환에 사용
// 뒤에 l 이 붙는 함수는 l 이 4 바이트를 의미하므로 IP주소의 변환에 사용
Endian Test
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
unsigned short host_port=0x1234;
unsigned short network_port;
network_port=htons(host_port);
unsigned long host_addr=0x12345678;
unsigned long network_addr;
network_addr=htonl(host_addr);
printf("Host ordered port: %#x \n", host_port); // 0x1234
printf("Network ordered port: %#x \n", network_port); // 0x3412 빅 엔디안
printf("Host ordered address: %#lx \n", host_addr); // 0x12345678
printf("Network ordered address: %#lx \n", network_addr); // 0x78563412 빅 엔디안
// 16진수 2개는 1바이트다.
return 0;
}
리틀 엔디 안 기준으로 정렬하는 CPU에 서의 실행결과이다.
만약 빅 엔디안 CPU에서 실행을 했다면 변환 후에도 값은 달라지지 않는다.
문자열 IP 주소를 네트워크 바이트 순서(빅 엔디안)로 변환
inet_addr( ) vs inet_aton( ) 사용
sockaddr _in
struct sockaddr_in
{
sa_family_t sin_family; // 주소체계(Address Family)
uint16_t sin_port; // 16 비트 Port 번호
struct in_addr sin_addr; // 32 비트 IP주소, 빅 엔디안
char sin_zero[8]; // 사용되지 않음
}
IP주소 211.214.107.99와 같은 ‘십진수 표현방식(Dotted Decimal Notation)'을 네트워크 바이트 순서의 IP가 변환한다.
방법 1) inet_addr( )
#include <arpa/inet.h>
in_addr_t inet_addr(const char * string);
//성공시 빅 엔디안으로 변환된 32 비트 정수 값, 실패시 IN ADDR_NONE 반환
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
char *addr1="127.212.124.78";
char *addr2="127.212.124.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); // 0x4030201
conv_addr=inet_addr(addr2);
if(conv_addr==INADDR_NONE)
printf("Error occureded \n");
else
printf("Network ordered integer addr: %#lx \n\n", conv_addr);
return 0;
}
방법 2) inet_aton( )
#include <arpa/inet.h>
int inet_aton(const char * string , struct in_addr *addr);
// 성공 시 1(true) , 실패 시 0(false) 반환
// string 변환할 IP삽 정렬 담고 있는 문자열의 주소 값 전달
// addr 변환된 정보를 저장할 in_addr 구조체 변수의 주소 값 전달
#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);
}
네트워크 바이트 순서(빅 엔디안)를 문자열 IP 주소로 변환
inet_ntoa( )
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr adr);
// 성공시 변완된 문자열의 주소값, 실패시 -1 반환
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
struct sockaddr_in addr1, addr2;
char *str_ptr;
char str_arr[20];
addr1.sin_addr.s_addr=htonl(0x1020304);
addr2.sin_addr.s_addr=htonl(0x1010101);
str_ptr=inet_ntoa(addr1.sin_addr);
strcpy(str_arr, str_ptr);
printf("Dotted-Decimal notation1: %s \n", str_ptr);
inet_ntoa(addr2.sin_addr);
printf("Dotted-Decimal notation2: %s \n", str_ptr);
printf("Dotted-Decimal notation3: %s \n", str_arr);
return 0;
}
/*
root@com:/home/swyoon/tcpip# gcc inet_ntoa.c -o ntoa
root@com:/home/swyoon/tcpip# ./ntoa
Dotted-Decimal notation1: 1.2.3.4
Dotted-Decimal notation2: 1.1.1.1
Dotted-Decimal notation3: 1.2.3.4
*/
printf("Client %s:%d is connected..\n",inet_ntoa(clntAddr.sin_addr), ntohs(clntAddr.sin_port));
printf("Client(%d) : %s \n", ntohs(clntAddr.sin_port),rBuff);
printf("Client(%d): is disconnected\n", ntohs(clntAddr.sin_port));
문자열 주소 초기화 방법
struct sockaddr_in addr;
char *serv_ip="211.217.168.13"; // IP주소 문자열 선언
char *serv_port="9190"; // PORT 번호 문자열 선언
memset(&addr, 0, sizeof(addr)); // 구조체 변수 addr의 모든 멤버 8으로 초기화
addr.sin_family=AF_INET; // 주소체계 지정
addr.sin_addr.s_addr=inet_addr(serv_ip); // 문자열 기반의 IP주소 초기화
addr.sin_port=htons(atoi(serv_port)); // 문자열 기반의 PORT 변호 초기화
서버의 sockaddr_in 구조체 변수에 IP 211. 217 .168 .13 , PORT 9190로 들어오는 요청을 받겠다.
INADDR ANY
struct sockaddr_ in addr;
char *serv_port= "9190";
memset(&addr, 0, sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(serv_port));
서버 프로그램의 구현에 많이 선호되는 방법으로 INADDR_ANY라는 이름의 상수를 통해서 IP주소를 자동 할당한다.
두 개 이상의 IP를 할당 받아서 사용하는 경우 PORT변호만 일치하면 수신할 수 있게 된다.
클라이언트는 서버의 기능을 포함하는 경우가 아니라면, 사용될 일이 별로 없다.
출처 & 참고