본문 바로가기

Programming Language/C&C++

소켓 통신 #2 - AF_INET 사용하기 (2)

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

소켓 통신 #2 - AF_INET 사용하기 (2)

- by Tapito


이번에는 서버가 되어 접속 요청을 받아들여보겠습니다.

클라이언트에서는 접속 대상의 sockaddr_in 구조체 세팅 - socket() 함수 - connect() 함수의 3단계로 접속이 진행된 반면,
서버는 자신의 sockaddr_in 구조체 세팅 - socket() 함수 - bind() 함수 - listen() 함수 - accept() 함수의 5단계로 접속이 진행됩니다.

1단계. sockaddr_in 구조체 설정하기

sockaddr_in 구조체 설정은 클라이언트와 크게 다르지 않습니다. 다만 자신의 IP 주소를 명시하지 않는다는 차이가 있습니다.

/* 사용 예 */
struct sockaddr_in sockaddr_internet;
 
/* 주소 체계로서 IPv4를 사용하는 인터넷망에 접속 */
sockaddr_internet.sin_family = AF_INET;
/* 접속할 대상을 지정하지 않음 */
serveraddr.sin_addr.s_addr = inet_addr(INADDR_ANY);
/* 포트 번호는 1435 */
serveraddr.sin_port = htons(1435);

2단계. socket 생성하기

네트워크에 접속하기 위한 소켓을 생성합니다. 이 역시 클라이언트 측에서 생성한 방식과 같습니다.

/* 사용 예 */
SOCKET sock;
/* 인터넷에 접속하여 스트림 방식으로 입출력하는 소켓 생성 */
sock = socket(AF_INET, SOCK_STREAM, 0);
/* 소켓 만들기에 실패 시 INVALID_SOCKET이 반환 */
if(sock == INVALID_SOCKET) printf("Invalid Socket");

3단계. 생성한 소켓을 접속을 받는 용도로 지정

bind 함수는 생성된 소켓에 지역 IP 주소와 포트 번호를 연결시켜 서버용 소켓으로 만들어주는 역할을 합니다.

int bind(SOCKET s, const struct sockaddr * name, int namelen);

s: 서버용으로 사용할 소켓입니다.

name: 자기 자신의 sockaddr 구조체입니다.

namelen: 여기에서는 sockaddr_in 구조체의 크기입니다.

/* 사용 예 */
int state;
state = bind(sock , (struct sockaddr *)&sockaddr_internet, sizeof(sockaddr_in));
if (state == SOCKET_ERROR) printf("Socket Error");

4단계. 접속이 들어올때까지 대기

서버로서 갖추어야 할 준비는 모두 끝났습니다. 이제 손님이 접속할 때까지 계속 기다리기만 하면 됩니다. 이 역할을 하는 함수는 listen() 함수입니다.

int listen(SOCKET s, int backlog);

s: 서버용으로 쓰는 소켓입니다.

backlog: 최대 대기자 수에 해당하는 정수 값입니다. 이 값의 기준은 따로 정해지지 않았고 경험적으로 5 정도면 적당하다고 알려져 있습니다.

/* 사용 예 */
int state;
/* 접속자가 들어올때까지 내부적으로 무한루프를 돌리며 계속 이 단계에서 머물게 될 것입니다. */
state = listen(sock, 5);
/* listen도 오류시 SOCKET_ERROR를 반환합니다. 접속자 1명을 성공적으로 받아들였다면 0이 반환될 것입니다. */
if(state == SOCKET_ERROR) printf("Socket Error");

5단계. 자신에게 접속한 유저의 sockaddr_in 얻기

서버로 열어두고 접속을 기다렸으면 자신에게 들어온 유저가 누구인지를 알 필요가 있습니다. 클라이언트가 connect 함수로 자신에게 접속하면서 클라이언트 자신의 sockaddr_in 구조체의 내용도 서버에게 전해지는데 이 역할은 accept 함수에서 수행합니다.

SOCKET accept(SOCKET s, struct sockaddr * addr, int * addrlen);

s: 서버용으로 쓰고 있는 소켓입니다.

addr: 접속 유저에 대한 정보를 저장할 sockaddr 구조체 포인터입니다.

addrlen: 접속된 유저에 대한 정보가 몇 바이트인지도 이 변수를 통해 얻을 수 있습니다.

반환 값으로 접속자 1인에 대한 소켓이 반환됩니다.

/* 사용 예 */
SOCKET sockClient;
struct sockaddr_in sockaddrClient;
int sockaddrSize = 0;
sockClient = accept(sock, (struct sockaddr *)&sockaddrClient, &sockaddrSize);
if(sockClient == INVALID_SOCKET) printf("Invalid Socket");
else printf("접속자 IP는 %s이고 구조체 크기는 %d바이트입니다.", inet_ntoa(sockaddrClient.sin_addr), sockaddrSize);