기존의 에코 서버는 한 번에 하나의 클라이언트만 처리할 수 있습니다. 이는 여러 클라이언트가 동시에 연결을 시도하면 첫 번째 클라이언트가 작업을 완료할 때까지 다른 클라이언트가 대기 상태에 놓이는 문제를 초래합니다. 멀티스레드 에코 서버는 이러한 문제를 해결하기 위해 각 클라이언트 연결마다 별도의 스레드를 생성하여 동시에 여러 클라이언트를 처리할 수 있도록 설계됩니다.
. 기존 에코 서버의 문제점
1.1 단일 스레드 구조
기존 서버는 단일 스레드로 동작하며, 다음과 같은 문제가 있습니다:
- accept 블로킹:
- 첫 번째 클라이언트가 연결되면 accept 이후의 로직에서 해당 클라이언트만 처리.
- 다중 클라이언트 지원 불가:
- 추가 클라이언트는 기존 클라이언트의 작업이 완료될 때까지 대기.
2. 멀티스레드 에코 서버의 필요성
2.1 동시성 제공
- 각 클라이언트 요청에 대해 별도의 스레드를 생성하여 처리.
- 클라이언트는 대기 없이 즉각적인 연결과 서비스를 받을 수 있음.
2.2 워커 스레드 구조
- 메인 스레드:
- accept를 통해 클라이언트의 연결을 수락.
- 새로운 클라이언트 연결 시 워커 스레드 생성.
- 워커 스레드:
- 생성된 스레드는 해당 클라이언트와 통신(recv/send)을 담당.
3. 멀티스레드 에코 서버 구현
3.1 메인 스레드 코드
메인 스레드는 클라이언트 연결을 수락하고, 워커 스레드를 생성합니다:
while ((hClient = ::accept(hSocket, (SOCKADDR*)&clientaddr, &nAddrLen)) != INVALID_SOCKET)
{
puts("새 클라이언트가 연결되었습니다.");
// 클라이언트 처리를 위한 워커 스레드 생성
HANDLE hThread = ::CreateThread(
NULL, // 보안 속성
0, // 기본 스택 크기 (1MB)
ThreadFunction, // 실행할 스레드 함수
(LPVOID)hClient, // 클라이언트 소켓
0, // 기본 생성 플래그
&dwThreadID); // 생성된 스레드 ID
// 스레드 핸들 닫기
::CloseHandle(hThread);
}
3.2 워커 스레드 코드
워커 스레드는 클라이언트와 통신을 담당하며, 연결이 끊어질 때 소켓을 닫습니다:
DWORD WINAPI ThreadFunction(LPVOID pParam)
{
SOCKET hClient = (SOCKET)pParam;
char szBuffer[128] = { 0 };
int nReceive = 0;
puts("새 클라이언트가 연결되었습니다.");
// 클라이언트와 통신
while ((nReceive = ::recv(hClient, szBuffer, sizeof(szBuffer), 0)) > 0)
{
// 수신한 데이터를 그대로 반향(Echo) 전송
::send(hClient, szBuffer, nReceive, 0);
puts(szBuffer);
memset(szBuffer, 0, sizeof(szBuffer));
}
puts("클라이언트 연결이 끊겼습니다.");
::closesocket(hClient);
return 0;
}
4.1 메인 스레드
- 클라이언트 연결 요청을 수락(accept).
- 연결된 클라이언트에 대해 워커 스레드를 생성.
- 생성된 스레드에 클라이언트 소켓을 전달.
4.2 워커 스레드
- 클라이언트와 데이터를 송수신(recv/send).
- 연결이 끊어지면 소켓 자원을 해제.
. 멀티스레드 서버의 장점
- 동시성 제공:
- 여러 클라이언트를 동시에 처리 가능.
- 한 클라이언트의 작업이 다른 클라이언트에 영향을 주지 않음.
- 확장성:
- 클라이언트 수가 증가하더라도 성능 저하를 최소화.
- 사용자 경험 개선:
- 빠른 응답 속도와 대기 없는 연결 제공.
6. 주의사항
6.1 스레드 관리
- 워커 스레드의 생성과 종료를 효율적으로 관리해야 함.
- 무한정 스레드를 생성하면 시스템 자원이 고갈될 수 있으므로 스레드 풀(Thread Pool) 구현을 고려.
6.2 공유 자원 관리
- 여러 스레드가 동일한 자원을 사용할 경우 경쟁 상태(Race Condition) 발생 가능.
- 뮤텍스(Mutex), 크리티컬 섹션(Critical Section) 등을 사용해 동기화 필요.
6.3 메모리 누수 방지
- 스레드 종료 후 소켓 자원(closesocket)과 스레드 핸들(CloseHandle)을 반드시 해제.
'프로그래밍 > 소켓 프로그래밍 입문' 카테고리의 다른 글
채팅 서버와 메시지 브로드캐스팅: IP 주소 문제와 해결 (0) | 2025.01.14 |
---|---|
멀티스레드 채팅 서버: 구조와 이론 (0) | 2025.01.14 |
서버 소켓과 SO_REUSEADDR (0) | 2025.01.13 |
소켓 입/출력 버퍼와 TCP_NODELAY (0) | 2025.01.13 |
send와 recv는 1대1로 매핑되어 동작하는가? (0) | 2025.01.13 |