01. 바이트 정렬 함수
1) 바이트 정렬(byte ordering)이란?
→ 1바이트 이상의 데이터를 정렬하는 방식
→ 메모리에 데이터를 저장할 때 바이트 순서로, 빅 엔디안 & 리틀 엔디안 방식이 있다.
- 시스템에서 사용하는 바이트 정렬 방식은 CPU와 운영체제에 따라 다르다.
2) 바이트 정렬 종류

- 빅 엔디안(big-endian)
- 최상위 바이트(MSB, Most Significant Byte)부터 차례로 저장하는 방식
- ex) 0x12345678이라는 32bit 크기의 데이터가 존재할 경우, 0x12, 0x34, 0x56, 0x78이라고 읽는다.
- → AMD계열의 CPU에서 이용하는 방식으로, 낮은 주소에 데이터의 가장 높은 바이트부터 정렬하며, 사람이 읽기 좋은 정렬 방식
- 최상위 바이트(MSB, Most Significant Byte)부터 차례로 저장하는 방식
2. 리틀 엔디안(little-endian)
- 최하위 바이트(LSB, Least Significant Byte)부터 차례로 저장하는 방식
- 평소 우리가 숫자를 사용하는 선형 방식과는 반대로 거꾸로 읽어야 함
- ex) 0x12345678 이라는 32bit 데이터가 존재할 경우, 다음과 같다.

- ex) 0x12345678 이라는 32bit 데이터가 존재할 경우, 다음과 같다.
- → 인텔 게열의 CPU에서 이용하는 바이트 오더링 방식으로, 낮은 주소에 데이터의 낮 (LSB, Least Significant Bit)가장 낮은 바이트부터 정렬하는 방식
02. 바이트 정렬 방식을 고려해야하는 경우 두 가지 관점
- 프로토콜 구현
- 응용 프로그램 데이터
1) 프로토콜 구현을 위해 필요한 정보
- IP 주소의 바이트 정렬 방식에 따른 문제점
- 호스트가 보낸 패킷의 IP 헤더에는 IP 주소가 포함되어 있다. 이 패킷을 라우터가 받으면 IP 주소를 찹조해 다음 위치에 있는 라우터에 보낸다. 이때 호스트와 라우터가 IP 주소의 바이트 정렬 방식을 약속하지 않으면, IP 주소 해석이 달라서 라우팅에 문제가 발생할 수 있다.

2. 포트 번호의 바이트 정렬 방식에 따른 문제점
- 두 호스트가 포트 번호의 바이트 정렬 방식을 약속하지 않으면, 포트 번호 해석이 달라져 데이터가 잘못된 목적지 프로세스에 전달될 수 있다

⇒ 바이트 정렬 방식(호스트 바이트 정렬(host byte ordering)이 통일되어 있지 않아서 이러한 문제들이 발생한다
⇒ 해결법 : IP 주소와 포트번호의 바이트 정렬 방식을 빅 엔디안으로 통일해 사용한다. (리틀엔디안도 괜찮음. 통일만 시키자)
- 네트워크 용어로서 빅 엔디안: 네트워크 바이트 정렬(network byte ordering)
- 참고
- 네트워크 바이트 정렬(Network byte ordering) : 빅 엔디안 방식
- 호스트 바이트 정렬(host byte ordering) : 시스템이 사용하는 고유한 바이트 ㅈ정렬 방식.
2) 응용 프로그램이 주고받는 데이터
- 데이터의 바이트 정렬 방식에 따른 문제점
- 두 호스트가 주고받는 데이터에 대해 byte ordering(바이트 정렬)을 약속하지 않으면, 데이터 해석 문제가 발생할 수 있다.

⇒ 해결법 : 대개 네트워크 바이트 정렬(빅 엔디안) 방식을 사용한다.
- 서버와 클라이언트를 같이 제작하는 경우이거나, 서로 다른 기종간 데이터를 교환하는 경우 상호 약속이 필요하다.
03. 바이트 정렬 함수
- 앞에 H가 붙으면 host
- 앞에 N이 붙으면 Network
- 마지막에 붙어있는 s, l은 short이냐 long이냐를 판단하는것.
1) 정렬 함수
- 유닉스 호환 바이트 정렬 함수
-
#include <winsock2.h> u_short htons(u_short hostshort); // host-to-network-short u_long htonl(u_long hostlong); // host-to-network-long u_short ntohs(u_short netshort); // network-to-host-short u_long ntohl(u_long netlong); // network-to-host-long return - > 반환된 결과 - htons: 호스트 바이트 정렬로 저장된
16비트값을 입력으로 받아 → 네트워크 바이트 정렬로 변환한 값을 리턴 - htonl: 호스트 바이트 정렬로 저장된
32비트값을 입력으로 받아 → 네트워크 바이트 정렬로 변환한 값을 리턴 - ntohs: 네트워크 바이트 정렬로 저장된
16비트값을 입력으로 받아 → 호스트 바이트 정렬로 변환한 값을 리턴 - ntohl: 네트워크 바이트 정렬로 저장된
32비트값을 입력으로 받아 → 호스트 바이트 정렬로 변환한 값을 리턴
-
- 윈속 확장 바이트 정렬 함수
-
//소켓 디스크립터, ... , 반환된 결과 저장할 곳 int WSAHtons(SOCKET s, u_short hostshort, u_short *lpnetshort); int WSAHtonl(SOCKET s, u_long hostlong, u_long *lpnetlong); int WSANtohs(SOCKET s, u_short netshort, u_short *lphostshort); int WSANtohl(SOCKET s, u_long netlong, u_long *lphostlong); - 윈속 확장 함수이다.
- SOCKET s : 소켓 디스크립터
- *lpnetshort : 반환된 결과 저장할 곳
-
2) 바이트 정렬 함수 사용 그림

htons(),htonl()함수는 응용 프로그램이 소켓 함수에 데이터를 넘겨주기 전에 호출ntohs(),ntohl()함수는 소켓 함수가 결과로 리턴한 데이터를 응용프록램이 출력 등의 목적으로 사용하기 전에 호출됨
3) SOCKADDR_IN , SOCKADDR_IN6 구조체의 바이트 정렬 방식
- TCP/IP에서 사용할 구조체들은 다음과 같은 바이트 정렬 방식을 따른다.

04. 실습
- hton() 함수를 이용해 바이트 정렬을 바꾸고, 다시 ntoh() 함수로 원래 바이트 정렬로 되돌린것이다.

#pragma comment(lib, "ws2_32")
#include <winsock2.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
return 1;
u_short x1 = 0x1234;
u_long y1 = 0x12345678;
u_short x2;
u_long y2;
// 호스트 바이트 -> 네트워크 바이트
printf("[호스트 바이트 -> 네트워크 바이트]\n");
printf("0x%x -> 0x%x\n", x1, x2 = htons(x1));
printf("0x%x -> 0x%x\n", y1, y2 = htonl(y1));
// 네트워크 바이트 -> 호스트 바이트
printf("\n[네트워크 바이트 -> 호스트 바이트]\n");
printf("0x%x -> 0x%x\n", x2, ntohs(x2));
printf("0x%x -> 0x%x\n", y2, ntohl(y2));
// 잘못된 사용 예
printf("\n[잘못된 사용 예]\n");
printf("0x%x -> 0x%x\n", x1, htonl(x1)); //short인에 long을 썼다.
WSACleanup();
return 0;
}
노션에서 정리한걸 티스토리로 옮기려니 색도 다 날라가고 마크다운이 깨지는게 조금 있네요 ㅜ
'Computer Science > TCP&IP 소켓 프로그래밍' 카테고리의 다른 글
| 3-4. 도메인 이름 시스템(DNS)과 이름 변환 함수, hostent 구조체, DomainName, gethostbyname() , gethostbyaddr() (1) | 2023.12.28 |
|---|---|
| 3-3. IP 주소 변환 함수 ( inet_addr, inet_ntoa, WASAddressToString, WSAStringToAddress) (0) | 2023.11.25 |
| 3-1. 소켓 주소 구조체 다루기 (SOCKADDR, IN_ADDR, sockaddr_in, (0) | 2023.11.13 |
| 1-2. 소켓(socket)의 개념 (윈도우 소켓) (0) | 2023.09.26 |
댓글