bind()는 socket
으로 통신하는 대상의 주소를 할당하는 함수이다.
// socket 생성 및 bind 코드
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in s_in = {};
s_in.sin_family = AF_INET;
s_in.sin_addr.S_un.S_addr = INADDR_ANY;
s_in.sin_port = htons(50000);
if (bind(s, reinterpret_cast<sockaddr*>(&s_in), sizeof(s_in)) == SOCKET_ERROR) {
cout << "bind error: " << WSAGetLastError() << endl;
return;
}
위 코드에서 bind() 함수 실행 시 sockaddr_in 타입의 s_in 변수를 sockaddr* 타입으로 캐스팅하는 것을 볼 수 있다. 그러면 bind 시 필요한 건 sockaddr* 타입의 데이터인데 왜 sockaddr 타입의 변수를 초기화 하지 않고 sockaddr_in 타입으로 변수를 초기화한 걸까?
bind의 2번째 인자로 필요한 것은 통신 대상의 주소이다. sockaddr 구조체는 주소를 담는 구조체로, 모양은 아래와 같다.
struct sockaddr {
ADDRESS_FAMILY sa_family;
CHAR sa_data[14]; // Up to 14 bytes of direct address.
}
sa_family
변수에 할당되는 값은 AF_INET
, AF_INET6
와 같은 주소 체계이다.
이어서 오는 sa_data
14 byte에는 IP:PORT와 같은 주소가 담겨야 한다.
IPv4를 예로 보면, sa_family = AF_INET
, sa_data
에는 127.0.0.1
과 12345
같은 값이 들어가게 된다.
그런데 sockaddr
구조체 자체는 IPv4만이 아닌 IPv6에도 사용 가능하도록, 즉 다른 프로토콜에서도 sockaddr
구조체를 사용해서 socket
통신이 가능하도록 구현해 놓은 것이다. 그렇기 때문에 sockaddr
구조체의 데이터는 실제 사용되는 프로토콜에 따라 값이 다뤄지는 유형이 달라질 수 있다. 이러한 실정에서 프로토콜에 맞는 방식으로 값을 쉽게 작성하도록 돕는 게 sockaddr_in
과 같은 구조체이다. sockaddr_in
은 IPv4를 사용할 때에 사용하도록 되어 있다.