3-1 Internet Address
IP(Internet Address)
인터넷에 존재하는 호스트들을 구분하기 위한 32비트 주소 체계
점이 찍힌 십진수 표현 방식(Dotted-decimal Notation) : 211.217.10.9
클래스
Class A : 0.0.0.0 ~ 127.255.255.255, 1바이트는 네트워크 ID, 3바이트는 호스트 ID
Class B : 128.0.0.0 ~ 191.255.255.255, 2바이트는 네트워크 ID, 2바이트는 호스트 ID
Class C : 192.0.0.0 ~ 223.255.255.255, 3바이트는 네트워크 ID, 1바이트는 호스트 ID
Class D : 멀티캐스트 주소. 224.0.0.0 ~ 239.255.255.255
Class E : 예약됨. 240.0.0.0 ~ 255.255.255.255
IP 주소 : 네트워크 주소(네트워크를 구분지음) + 호스트 주소(호스트를 구분지음)
subnet mask에 따라서 네트워크 주소(ID)와 호스트 주소(ID)로 나누어진다.
※ 라우터는 네트워크 ID(주소)만 참조한다.
※ 루프백 주소 : 127.x.x.x, 네트워크 상으로 패킷을 전송하지 않고 자기자신에게 돌려준다.
3-2 Port란 무엇인가?
Port
호스트 내에서 실행되고 있는 프로세스를 구분 짓기 위한 16비트의 논리적인 값. (소켓에 할당)
논리적인 값 : 소프트웨어 적으로 구현.
Well-known ports : 0~1023
3-3 주소 정보의 표현
IPV4의 주소 체계를 나타내는 구조체
※ 하나의 프로토콜 내에 2개 이상 프로토콜이 있을 것을 대비해서 만든 구조체. ()는 윈도우즈용.
※ 모든 데이터는 네트워크 바이트 순서로 저장해야 한다.
struct sockaddr_in
{
sa_family_t(short) sin_family; // 주소 체계(address_family) - AF_INET
uint16_t(unsigned short) sin_port; // 16비트 TCP 혹은 UDP 포트
struct in_addr sin_addr; // 32비트 IPv4 주소
char sin_zero[8]; // 사용되지 않음. padding 용도
};
struct in_addr
{
uint32_t(unsigned long) s_addr; // 32비트 IPv4 인터넷 주소
};
3-4 네트워크 바이트 순서
0x12345678을 Big-Endian으로는 메모리 순서대로 0번지 0x12, 1번지 0x34, 2번지 0x56, 3번지 0x78
0x12345678을 Little-Endian으로는 메모리 순서대로 0번지 0x78, 1번지 0x56, 2번지 0x34, 3번지 0x12
형태로 저장.
호스트 바이트 순서
어떤 시스템(motorola, sun사 기계)은 Big-Endian, 어떤 시스템(intel사)은 Little-Endian을 사용하므로 일정하지 않다.
※ 데이터 표현 방식이 다르므로 다른 플랫폼끼리 문제가 발생한다.
네트워크 바이트 순서
Big-Endian 방식을 적용하기로 하였다.
바이트 순서 변환 함수
※ 어느 시스템에서 돌아갈 지 모르기 때문에 반드시 사용해야 한다.
unsigned short htons(unsigned short); // 호스트 -> 네트워크 short
unsigned short ntohs(unsigned short); // 네트워크 -> 호스트 short
unsigned long htonl(unsigned long); // 호스트 -> 네트워크 long
unsigned long ntohl(unsigned long); // 네트워크 -> 호스트 long
'h' : host byte order
'n' : network byte order
's' : short(16비트)
'l' : long(32비트)
3-5 인터넷 주소 조작하기
1. 점이 찍힌 십진수 방식(Dotted-Decimal Notation)을 Big-Endian 32비트 정수형 데이터로 바꿔주는 함수(네트워크)
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
unsigned long inet_addr(const char *string);
리턴 : 성공시 32비트 big-endian값, 실패시 INADDR_NONE
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
2. Big-Endian 32비트 정수형 데이터를 점이 찍힌 십진수 방식로 바꿔주는 함수(네트워크)
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
char* inet_ntoa(struct in_addr addr);
※ 따로 inet_ntoa 내부적으로 static 배열을 통해 주소에 대한 문자열이 존재한다.
3-6 인터넷 주소 초기화
struct sockaddr_in addr;
char *serv_ip = "...";
char *serv_port = "...";
memset(&addr, 0, sizeof(addr_len)); // 주소에 해당하는 구조체 값을 0으로 초기화. 좋은 습관.
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(serv_ip);
addr.sin_port = htons(atoi(serv_port));
INADDR_ANY?
addr.sin_addr.s_addr = htons(INADDR_ANY);
// 서버의 IP가 뭐더라? 답은 INADDR_ANY : 내 시스템의 IP 주소를 찾아 알아서 할당.
3-7 주소 정보 할당하기
리눅스
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, int addrlen);
윈도우즈
#include<winsock2.h>
int bind(SOCKET s, const struct sockaddr FAR *name, int namelen);
sockfd/s : 주소를 할당하고자 하는 소켓의 파일 디스크립터 인자/핸들 인자
myaddr : 할당하기를 원하는 주소 정보를 지닌 sockaddr_in 구조체 변수의 포인터.
※ sockaddr 형태로 형변환 해서 넘겨야 한다.
※ 프로토콜에 독립적으로 사용하기 위해서. void 포인터 보다 먼저 이 함수가 개발되었다.
※ FAR 포인터는 과거의 잔재. 현재는 무시한다.
addrlen/namelen : 주소 정보 구조체의 길이.
3-8 윈도우즈 기반으로 구현하기
※ SOCKADDR_IN = struct sockaddr_in
※ SOCKADDR = struct sockaddr
※ 서버 프로그래밍을 할 때는 htons(INADDR_ANY);를 사용한다.
3-9 WSAStringToAddress & WSAAddressToString
주소 정보를 나타내는 문자열을 가지고 주소 정보 구조체 변수를 채운다.
※ 윈도우즈 기반 주소 변환 함수.
#include<winsock2.h>
INT WSAStringToAddress(
LPTSTR AddressString, // 점이 찍힌 십진수 표현(포트정보 포함)과 같은 주소 정보 문자열 포인터.
INT AddressFamily, // 주소 정보 문자열이 속한 주소 체계(AF_INET)
LPWSAPROTOCOL_INFO lpProtocolInfo, // 프로토콜 제공자를 설정. 일반적으로 NULL.
LPSOCKADDR lpAddress, // 주소 정보 구조체 변수 포인터
LPINT lpAddressLength); // lpAddress 포인터가 가리키는 버퍼의 크기.
리턴 : 성공시 0, 실패시 SOCKET_ERROR
#include<winsock2.h>
INT WSAAddressToString(
LPSOCKADDR lpsaAddress, // 문자열로 변환할 주소 정보를 가진 구조체 포인터
dwAddressLength, // lpsaAddress 포인터가 가리키는 변수의 크기
LPWSAPROTOCOL_INFO lpProtocolInfo, // 프로토콜 제공자를 설정. 일반적으로 NULL.
LPTSTR lpszAddressString, // 문자열로 변경된 결과를 저장할 버퍼 포인터.
LPDWORD lpdwAddressStringLength); // lpszAddressString 버퍼의 크기.
리턴 : 성공시 0, 실패시 SOCKET_ERROR