소켓 주소

|

모든 패킷에는 보내는 주소와 받는 주소가 필요합니다. 특히, 전송 계층의 패킷은 추가로 발신지와 수신지의 포트도 필요합니다. 이러한 정보는 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는 메모리 레이아웃상 sockaddrsa_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 값으로 변환해주는 함수입니다.