서버보다 단순한 이유
흐름 비교
| 구분 | 서버 | 클라이언트 |
|---|
| 기본 흐름 | socket -> bind -> listen -> accept | socket -> connect |
| 주소/포트 할당 | 서버가 명시적으로 bind | 클라이언트는 OS 자동 할당 가능 |
| 접속 대기 | 필요 | 불필요 |
자동 할당(에페메럴 포트)
- 클라이언트는 보통
bind를 생략합니다.
- OS가 적절한 로컬 IP와 임시 포트(ephemeral port)를 자동으로 선택합니다.
- 서버
accept에서 보이는 clientAddr는 이 자동 할당 결과입니다.
bind가 필요한 예외 상황
- 특정 NIC/IP로만 나가야 하는 멀티홈 환경
- 송신 포트를 고정해야 하는 테스트/방화벽 정책
- 일반 게임 클라/더미 클라 실습에서는 대부분 불필요
socket() - 클라이언트 소켓 생성
코드
SOCKET clientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSock == INVALID_SOCKET) {
int err = WSAGetLastError();
return false;
}
핵심 포인트
- 서버와 같은 TCP 소켓 타입을 사용합니다.
- 실패하면 이후
connect는 시도하지 말고 즉시 에러 처리합니다.
connect() - 서버 연결 요청
권장 코드
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(7777);
int pton = inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
if (pton != 1) {
closesocket(clientSock);
return false;
}
if (connect(clientSock,
reinterpret_cast<sockaddr*>(&serverAddr),
sizeof(serverAddr)) == SOCKET_ERROR) {
int err = WSAGetLastError();
closesocket(clientSock);
return false;
}
인자 해석
| 항목 | 의미 |
|---|
| 주소 | 접속 대상 서버 IP |
| 포트 | 서버 listen 포트와 반드시 일치 |
| 반환 | 성공 0, 실패 SOCKET_ERROR |
블로킹 특성
- 기본(블로킹) 소켓에서
connect는 완료/실패가 결정될 때까지 대기할 수 있습니다.
- 네트워크 상태가 나쁘면 체감상 "멈춘 것처럼" 보일 수 있습니다.
- 이 문제를 줄이기 위해 이후 Part에서 non-blocking/이벤트 기반 접근을 다룹니다.
루프백과 실제 주소 구분
| 시나리오 | 주소 예시 |
|---|
| 같은 PC 로컬 테스트 | 127.0.0.1 |
| 같은 LAN 내부 테스트 | 192.168.x.x, 10.x.x.x |
| 외부 서비스 접속 | 공인 IP 또는 도메인 |
실무 주의점
127.0.0.1은 "항상 자기 자신"입니다. 다른 PC 서버에 접속할 수 없습니다.
- 사설망 주소(
192.168.x.x)는 라우터/NAT 밖에서 직접 접근되지 않을 수 있습니다.
- 운영 환경에서는 보통 도메인 + DNS(
getaddrinfo)를 사용합니다.
connect 실패 시 자주 보는 원인
| 에러 코드 | 의미 | 점검 항목 |
|---|
WSAECONNREFUSED (10061) | 대상 포트가 열려 있지 않음 | 서버 실행 여부, 포트 번호 |
WSAETIMEDOUT (10060) | 연결 시간 초과 | 방화벽, 경로, 서버 응답 지연 |
WSAEHOSTUNREACH (10065) | 호스트 도달 불가 | IP 오타, 라우팅/NAT |
WSAEADDRNOTAVAIL (10049) | 주소 사용 불가 | 잘못된 로컬/원격 주소 설정 |
진단 루틴
- 서버가 해당 포트에서 listen 중인지 확인
- IP/포트 문자열과
inet_pton 반환값 확인
- 방화벽/보안 정책 확인
- 로그에 함수명 + 코드 + 대상 주소를 함께 기록
강의 시 유의사항
강조 포인트
- 클라이언트의 핵심은 "간단함"이 아니라 "자동 할당과 connect 성공 조건 이해"입니다.
bind 생략 이유(에페메럴 포트)를 반드시 설명하세요.
- 루프백/사설망/공인망 주소를 혼동하지 않도록 사례 중심으로 구분하세요.
시연 추천
- 서버 미실행 상태에서
connect 실패(10061) 재현
- 서버 실행 후 동일 코드 재시도해 성공 비교
127.0.0.1과 LAN IP를 바꿨을 때 동작 차이 확인
체크 질문 (스스로 답해보기)
- 클라이언트는 왜 보통
bind를 호출하지 않아도 되는가?
connect가 실패했을 때 첫 번째로 확인해야 할 3가지는 무엇인가?
127.0.0.1과 192.168.x.x의 의미 차이를 설명할 수 있는가?