출처: TCP/IP 윈도우 소켓 프로그래밍(https://product.kyobobook.co.kr/detail/S000001636201)
멀티스레드를 이용해 여러 클라이언트를 동시에 처리할 수 있는 TCP 서버를 작성할 것이다. 기본 골자는 다음과 같다.
DWORD WINAPI ProcessClient(LPVOID arg) {
// 전달된 소켓
SOCKET client_sock = (SOCKET)arg;
// 클라이언트 정보 얻기
addrlen = sizeof(clientaddr);
getpeername(client_sock, (SOCKADDR*)&clientaddr, &addrlen);
// 클라이언트와 통신
while(1) {
// ..
}
}
int main() {
while(1) {
// 클라이언트 접속 수용
client_sock = accept(listen_sock, ...);
// 스레드 생성
CreateThread(NULL, 0, ProcessClient, (LPVOID)client_sock, 0, NULL);
}
}
- 서버가
listening
상태로 대기하면서 클라이언트의 접속을 기다린다. 클라이언트가 접속하면accept
함수에서 클라이언트와 통신할 수 있는 소켓을 리턴한다. - 클라이언트와 통신을 하는 스레드를 생성한다. 이때 클라이언트 소켓을 스레드 함수에 인자로 전달한다.
getpeername
함수를 호출해 클라이언트의 IP 주소와 포트 번호를 얻는다. 클라이언트의 정보 출력을 원할 때 사용하면 된다.
스레드 함수에 소켓만 인자로 전달하는 경우에는 클라이언트의 주소 정보가 없으므로, getpeername
과 같이 소켓을 통해 주소 정보를 얻는 기능이 필요하다.
int getpeername(
SOCKET s,
struct sockaddr* name,
int* namelen
);
int getsockname(
SOCKET s,
struct sockaddr* name,
int* namelen
);
getpeername
함수는 원격 IP 주소와 원격 포트 번호를 리턴한다.getsockname
함수는 지역 IP 주소와 지역 포트 번호를 리턴한다.
멀티스레드 기반의 TCP 서버를 사용하면 클라이언트는 먼저 접속된 클라이언트 여부에 상관없이 항상 독립적으로 서비스를 제공 받을 수 있다. 또한 클라이언트 접속을 서버에서 멀티스레드로 관리하기 때문에 클라이언트의 코드는 거의 변하지 않는다.
// 클라이언트와 데이터 통신을 하기 위한 스레드 함수
DWORD WINAPI ProcessClient(LPVOID arg) {
SOCKET client_sock = (SOCKET)arg;
int retval;
SOCKADDR_IN clientaddr;
int addrlen;
char buf[BUFSIZE+1];
// 클라이언트 정보 얻기
addrlen = sizeof(clientaddr);
getpeername(client_sock, (SOCKADDR*)&clientaddr, &addrlen);
while(1) {
retval = recv(client_sock, buf, BUFSIZE, 0);
if(retval == SOCKET_ERROR) {
err_display("recv()");
break;
}
else if(retval == 0)
break;
// 받은 데이터 출력
buf[retval] = '\0';
printf("[TCP %s:%d] %s\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), buf);
// 데이터 보내기
retval = send(client_sock, buf, retval, 0);
if(retval == SOCKET_ERROR)
{
err_display("send()");
break;
}
}
closesocket(client_sock);
return 0;
}
서버에서는 accept
를 통해 클라이언트 접속을 수용하면 ProcessClient
함수를 스레드 시작점으로 사용하는 스레드를 만들어 클라이언트에 대한 서비스를 제공한다.
// accept
addrlen = sizeof(clientaddr);
client_sock = accept(client_sock, (SOCKADDR*)&clientaddr, &addrlen);
if(client_sock == INVALID_SOCKET) {
err_display("accept");
break;
}
// 스레드 생성
hThread = CreateThread(NULL, 0, ProcessClient, (LPVOID)client_sock, 0, NULL);
if(hThread == NULL) {
closesocket(client_sock);
}
else {
CloseHandle(hThread);
}
'네트워크 > 윈도우 소켓 프로그래밍' 카테고리의 다른 글
UDP 서버 클라이언트 개념 (0) | 2024.04.18 |
---|---|
스레드 동기화 - 임계 영역, 이벤트 (0) | 2024.04.08 |
멀티 스레드 서버를 위한 스레드 기초 (0) | 2024.04.08 |
응용 프로그램 데이터 전송 (0) | 2024.04.08 |
TCP 데이터 전송 함수 (0) | 2024.04.05 |