Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- handling
- Docker
- DEBUG
- container
- fread()
- 구조체
- ifdef
- fgets()
- signal
- pack
- 정보처리기사
- 필기
- 카운팅 정렬
- Wait
- 백준
- 자식
- half-close
- 2025
- struct
- scanf()
- 1929
- 에라토스테네스의 체
- sizeof()
- fork
- 10989
- fwrite()
- Kafka
- endif
- strlen()
- EOF
Archives
- Today
- Total
팥빵 먹으면서 코딩하는 블로그
03-2. [Linux/Ubuntu] 네트워크 프로그래밍 - 주소체계와 데이터 정렬 본문
주소체계와 데이터 정렬
- 인터넷 주소( = ip주소)
- 인터넷상에서 컴퓨터를 구분하는 목적으로 사용되는 주소
- 1. 네트워크 주소와 2. 호스트 주소로 나뉨
- subnet mask -> 네트워크 주소로 네트워크를 찾고, 호스트 주소를 사용해 해당 네트워크에서의 호스트를 검색함
- IPv4 인터넷 주소 체계에 따라 / 네트워크 id와 호스트 id의 사용할 수 있는 크기가 달라짐
- 클래스 별 네트워크 주소와 호스트 주소의 경계
- 클래스별 주소 범위
- 특수 범위 주소
- local loopback -> 127.0.0.1 : 본인 컴퓨터 주소
- 멀티캐스트 주소는 별도의 주소를 쓴다 : 클래스 D
- Port 번호
- IP 주소 : 컴퓨터를 구분하는 용도로 사용
- Port 번호
- 소켓(응용프로그램)을 구분하는 용도로 사용
- 하나의 프로그램 내에서는 둘 이상의 소켓이 존재할 수 있음
- ex. 원격접속 -> 예를 들어, 원격접속의 기능에 따라 4개의 포트가 할당됨
- Port 번호는 16비트로 표현 : 0 ~ 65535 이하
- Well-known Port 번호 : 0 ~ 1023 사이의 이미 용도가 정해진 포트 번호
- 우리가 쓸 수 없음. 미리 할당되어 있으므로 우리가 만든 응용 프로그램에 사용할 수 없음
- 그래서 우리가 9천번대 ex. 9190 포트번호...같은 걸 쓰는 거임
- IPv4 주소 표현을 위한 구조체
- IP 주소와 Port 번호는 sockaddr_in 구조체 변수를 이용해 표현
struct sockaddr_in
{
sa_family_t sin_family; // 주소체계
uint16_t sin_port; // Port 번호
struct in_addr sin_addr; // 32비트 IP 주소
char sin_zero[8]; // 사용되지 않음
};
struct in_addr //32비트 IP 주소
{
in_addr_t s_addr;
};
- 구조체 sockaddr_in의 멤버에 대한 분석
- sockaddr_in 구조체 멤버
- sin_familiy
- 주소체계 저장 : AF_INET
- sin_port
- 16비트 Port 번호 저장
- 네트워크 바이트 순서로 저장
- sin_addr
- 32비트 IP 주소 저장
- 네트워크 바이트 순서로 저장
- 멤버 sin_addr의 구조체 자료형 in_addr : 32비트 정수형
- sin_zero[8]
- ***반드시 0으로 초기화***
- sockaddr 구조체와 크기를 맞추기 위해 사용
- sin_familiy
- sockaddr_in 구조체 멤버
- 구조체 sockaddr_in의 활용
- bind()함수의 인자로 전달
- bind() 함수의 매개변수 타입이 sockaddr이므로 형변환이 필요
- d
- bind()함수의 인자로 전달
- 바이트 순서 (order)와 네트워크 바이트 순서
- 빅 에디안 (Big Edian)
- 상위 바이트의 값을 작은 번지수에 저장 (상위 바이트의 값을 앞에 위치)
- 리틀 에디안 (Little Edian)
- 하위 바이트의 값이 작은 번지수에 저장 (상위 바이트의 값을 큰 번지수에 저장)
- ex. Intel, Apple silicon ...
- 네트워크 바이트 순서 (Network Byte Order)
- 통일된 데이터 송수신 기준을 의미
- Big Edian 사용
- 데이터 송수신 과정
- 바이트 단위(바이트 스트림)로 데이터를 전송
- 바이트 변환 과정이 필요 없음
- ex.
- serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
- serv_addr.sin_port=htons(atoi(argv[1]));
- htonl, htons : ip주소와 port번호를 / little edian을 big edian으로 바꾸는 함수
- 따라서 네트워크 정보만 바꾸므로 / 실제 데이터는 변환 과정이 필요 없다!
- 빅 에디안 (Big Edian)
- htons() : host to network 방향으로 바꿔줌
- ntohs() : network to host 방향으로 바꿔줌
- 바이트 순서의 변환 (Edian Conversions)
- 바이트 순서 변환 함수
- 바이트 순서 변환은 sockaddr_in 구조체 변수에 주소 정보를 저장할 때만 사용하면 됨
- 바이트 순서 변환 함수
- htons에서
- h는 호스트 ~의미
- n은 네트워크
- s는 자료형
- htonl에서 l은 자료형 long을 의미
- 바이트 변환의 예시
#include <stdio.h>
#include <arpa/inet.h>
typedef unsigned short u16;
typedef unsigned long u32;
int main(int argc, char *argv[])
{
u16 host_port=0x1234;
u16 net_port;
u32 host_addr=0x12345678;
u32 net_addr;
net_port=htons(host_port); //htons 함수로 바이트 변환
net_addr=htonl(host_addr); //htonl 함수로 바이트 변환
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;
}
바이트 변환 함수
- 1. inet_addr() 함수
- inet_addr(const char *string)
- client 쪽에서 사용
- "211.214.107.99”와 같이 10진수(dotted decimal)형태로 표시된 문자열을 전달
- 빅 엔디안의 "정수"로 변환해줌
#include <arpa/inet.h>
in_addr_t inet_addr(const char* string);
-> 성공 시 빅 엔디안으로 변환된 32비트 정수값, 실패 시(비정상적인 주소일 경우) INADDR_NONE 반환 ***
-> 비정상적인 주소 ex. "1.2.3.256"
- in_addr_t 타입 : uint32_t 형태(32비트)
- 2. inet_aton (문자열 형태 -> in_addr 구조체로 변환)
- inet_aton() 함수
- inet_addr() 함수와 동일 기능
- 단, in_addr 구조체 변수에 변환 결과가 저장되는 점이 inet_addr()과 다름
- 한마디로 파라미터만 달라졌단 뜻
- inet_aton() 함수
#include <arpa/inet.h>
int inet_aton(const char *string, struct in_addr *addr);
-> *string에서 *addr로 변환 및 결과를 저장한다.
-> 성공 시 1(true), 실패 시 0(false) 반환
-> string : 변환할 IP 주소 정보를 담고 있는 문자열
-> ddr : 변환될 정보를 저장할 in_addr 구조체 변수의 주소값 전달
/***예시***/
char *addr="127.232.124.79"; //addr : 이 문자열을 가지고 있는 주소값
struct sockaddr_in addr_inet;
inet_aton(addr, &addr_inet.sin_addr)) //addr주소의 문자열을 &addr_inet.sin_addr로 전달
printf("Network ordered integer addr: %#x \n", addr_inet.sin_addr.s_addr)
- 3. inet_ntoa (정수 형태 -> 문자열 형태로 변환)
- inet_ntoa() 함수
- 네트워크 바이트 순서로 정렬된 정수형을 문자열 형태로 변환
- inet_aton() 함수의 반대 기능
- inet_ntoa() 함수
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr addr);
-> 성공 시 변환된 "문자열의 주소값", 실패 시 -1 반환
- 내부적으로 static 변수(char buffer[18])에 변환된 주소 값이 저장된다.
- 프로그램이 종료될 때까지 buffer 변수의 메모리는 유지됨
- 새로운 주소를 이용해 inet_ntoa()함수를 다시 호출할 경우,
- buffer가 가리키는 메모리(주소)의 값은 변경됨
- 기존이 buffer를 가리키고 있던 포인터 변수 *str_ptr의 값이 변경됨
- 따라서, 두 번째 inet_ntoa() 함수 호출시에는 리턴값을 받는 변수 사용 안함
- 전역 static 변수(참고)
- 해당 소스파일 내부에서만 사용 가능하고 다른 파일에서 사용 못함
- extern 사용 안됨!
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); //inet_ntoa 함수 내에서 메모리가 할당됨 -> 메모리가 없는 *str_ptr에 저장
strcpy(str_arr, str_ptr); // str_ptr의 값을 str_arr로 복사
printf("Dotted-Decimal notation1: %s\n", str_ptr);
str_ptr = inet_ntoa(addr2.sin_addr); // addr2.sin_addr : 내부 전역 buffer의 값 변경
printf("Dotted-Decimal notation2: %s\n", str_ptr); // str_ptr : return값이 없음에도 변경되어 있음
인터넷 주소의 초기화
- 인터넷 주소의 초기화
- 서버에서 주소 정보를 설정하는 이유 :
- “IP 211.217.168.13, PORT 9190으로 들어오는 데이터는 내게로 다 보내라!”
- 클라이언트에서 주소 정보를 설정하는 이유 :
- “IP 211.217.168.13, PORT 9190으로 연결을 해라!”
- 일반적인 인터넷 주소 초기화 과정
- 서버에서 주소 정보를 설정하는 이유 :
struct sockaddr_in addr;
char *serv_ip ="211.217.168.13"; // 서버의 IP 주소 문자열
char *serv_port = "9190"; // Port 번호 문자열
memset(&addr, 0, sizeof(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
- 서버 소켓의 주소 할당
- 이게 들어갈 곳에 하드코딩하면 다시 컴파일해서 다시 실행시켜야 하는 불편함이 있음
- 따라서 자동으로 할당해주는 INADDR_ANY를 사용
소켓에 인터넷 주소 할당
- 소켓에 인터넷 주소 할당
- bind()
- 소켓에 인터넷 주소 할당 과정
- 코드