소켓 주소
12 Aug 2017 | C++ 네트워크모든 패킷에는 보내는 주소와 받는 주소가 필요합니다. 특히, 전송 계층의 패킷은 추가로 발신지와 수신지의 포트도 필요합니다. 이러한 정보는 sockaddr
구조체에 정의되어 있습니다.
sockaddr 구조체
MacOS 용 sockaddr
struct sockaddr { __uint8_t sa_len; /* total length */ sa_family_t sa_family; /* [XSI] address family */ char sa_data[14]; /* [XSI] addr value (actually larger) */ };
Cygwin 용 sockaddr
struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ };
sa_family
는 주소의 종류를 나타내는 상수값입니다. sa_data
필드에 바이트(Byte) 형태로 주소값이 저장됩니다.
sa_data
에 들어갈 값은 sa_family
포맷에 따라 다양한 형태로 저장될 수 있기 때문에, 좀 더 편리하게 작성한 다음 캐스팅(Casting)을 통해 데이터를 변환하는 것을 추천합니다.
IPv4 패킷용 주소를 만들기 위해서는 sockaddr_in
자료형을 사용할 수 있습니다.
sockaddr_in 구조체
MacOS 용 sockaddr_in
struct sockaddr_in { __uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero[8]; };
Cygwin용 sockaddr_in
struct sockaddr_in { sa_family_t sin_family; /* Address family */ in_port_t sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; };
sin_family
는 메모리 레이아웃상 sockaddr
의 sa_family
와 같은 기능을 합니다.
sin_addr
는 4바이트의 IPv4 주소입니다. 소켓 라이브러리마다 조금씩 다르며, 어떤 플랫폼에서는 단순히 4바이트 정수이기도 합니다. 이 때문에 여러 플랫폼에서 여러 구조체를 유니온으로 감싸둔 구조체로 값을 지정할 수 있게 해 놓았습니다.
sin_len
은 플랫폼에 따라 존재하기도 하는 변수인데 구조체의 길이를 지정하는 변수입니다. 다음과 같은 형태로 사용하면 됩니다.
addr.sin_len = sizeof(addr);
바이트 주소 체계 변환
IP 주소를 바이트 형태로 묶어서 사용할 때 서버/클라이언트간 바이트 순서 체계가 다를 수 있습니다. 이런 문제를 방지하게 위해서 바이트 값들은 네트워크 순서 쳬계로 변경해서 사용해야 합니다.
uint16_t htons(uint16_t hostshort); uint32_t htonl(uint32_t hostlong);
htons()
는 부호 없는 16비트 정수를 받아서 네트워크 바이트 순서로 변환합니다. htonl()
은 32비트 정수에 대해서 변환을 수행합니다.
반대의 경우는
uint16_t ntohs(uint16_t networkshort); uint32_t ntohl(uint32_t networklong);
예제 코드
다음과 같은 코드 형태로 사용할 수 있습니다.
Server Address
struct sockaddr_in server_address; memset(server_address.sin_zero, 0, sizeof(server_address.sin_zero)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(80); server_address.sin_len = sizeof(server_address);
여기서 INADDR_ANY
는 서버로 들어오는 모든 데이터를 송수신하기위한 상수값입니다.
Client Address
struct sockaddr_in client_address; memset(client_address.sin_zero, 0, sizeof(client_address.sin_zero)); client_address.sin_family = AF_INET; client_address.sin_port = htons(80); client_address.sin_addr.s_addr = inet_addr("192.168.0.10"); client_address.sin_len = sizeof(client_address);
inet_addr()
함수의 경우, htonl()
을 사용할 필요 없이 문자열의 값을 long
값으로 변환해주는 함수입니다.