여러 스레드가 같은 소켓에 메시지를 WSASend로 보냅니다. 제출 순서와 완료 순서가 다를 때 메시지와 버퍼를 어떻게 관리하나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
애플리케이션 메시지 순서, 같은 소켓에 WSASend를 제출한 순서, 완료 통지를 처리한 순서는 서로 다를 수 있습니다. TCP는 실제로 제출된 바이트의 순서를 보장하지만 논리 메시지 경계를 만들어 주지 않으며, 여러 스레드가 헤더·본문을 나눠 보내면 다른 메시지 조각이 섞일 수 있습니다. 따라서 연결별 송신 큐와 버퍼 수명 소유자를 두고, 처음에는 한 연결의 제출 책임을 한곳에 모으겠습니다.
세 가지 순서를 분리한다
메시지 A와 B를 애플리케이션 큐에 넣은 순서가 곧 WSASend 호출 순서가 되려면 큐에서 프레임을 꺼내 제출하는 단일 소유자가 필요합니다. A의 헤더와 본문을 별도 호출로 보내는 사이 다른 스레드가 B를 제출하면 TCP는 A 헤더, B, A 본문 순서의 바이트를 그대로 전달할 수 있습니다. TCP의 신뢰성은 잘못된 애플리케이션 순서를 고쳐 주지 않습니다. 이를 연결별 직렬화(per-connection serialization)라고 부르는 정책으로 해결하겠습니다.
각 WSASend에는 독립된 OVERLAPPED와 실제 데이터의 수명을 유지하는 송신 컨텍스트가 필요합니다. WSABUF 배열 자체의 수명은 아래의 provider 캡처 계약과 구분합니다. 완료 통지가 제출 순서와 같다고 가정하지 않고, 완료된 바이트 수를 확인해 부분 전송이면 남은 오프셋을 기록합니다. 다만 한 소켓에서 여러 송신을 동시에 in-flight로 허용할 경우, Winsock이 보장하는 같은 소켓의 중첩 송신 순서와 애플리케이션 프레임 큐 정책을 확인하고, 완료가 뒤섞여도 다음 프레임 제출 조건이 깨지지 않게 해야 합니다.
버퍼·실패·느린 상대
송신 완료는 커널이 버퍼를 더 이상 사용하지 않는 경계이므로 그 전에는 원본 배열을 수정·재사용·해제하지 않습니다. 완료가 왔다고 상대 애플리케이션이 메시지를 처리했다는 뜻도 아닙니다. TCP ACK는 상대 TCP 스택의 수신 확인일 뿐, 서버 로직이나 DB 커밋을 확인하려면 애플리케이션 응답 ID가 필요합니다. 연결이 실패하면 아직 큐에 있는 메시지, 부분 전송된 메시지, 상대가 받았지만 응답하지 않은 메시지를 각각 어떤 상태로 기록할지 정합니다.
상대가 느리면 연결별 송신 큐가 무한히 커져 메모리를 고갈시킵니다. 메시지를 중요도별로 폐기할 수 있는지, 생산자를 막을지, 연결을 종료할지 프로토콜 계약으로 정하고 큐 바이트·in-flight 작업 수·최대 대기 시간을 제한합니다. 여러 생산자는 완성된 프레임을 큐에 원자적으로 넣고, 송신 소유자가 큐를 비우는 동안 새 프레임을 놓치지 않도록 wake-up 상태를 관리합니다.
테스트는 여러 스레드가 작은 헤더·본문과 큰 프레임을 동시에 제출하는 경우, 부분 전송, 완료 역순, 연결 종료·재접속을 포함합니다. 상대가 해석하는 바이트 스트림이 프레임 순서를 지키는지, 작업별 버퍼가 마지막 완료 뒤에만 해제되는지, 재접속 때 미확인 메시지가 idempotency key나 메시지 ID로 중복 처리되지 않는지를 확인하겠습니다.
WSASend의 중첩 I/O에서는 Winsock provider가 호출이 반환되기 전에 WSABUF 구조체 배열을 캡처하므로 배열 자체는 스택에 둘 수 있습니다. 하지만 배열이 가리키는 실제 페이로드와 OVERLAPPED는 완료까지 유효해야 하며, 완료 전에는 페이로드를 수정·재사용·해제하지 않겠습니다. 부분 완료 시에는 어느 데이터까지 소비됐는지에 따라 오프셋을 갱신하겠습니다. 송신 직렬화는 TCP의 전송 순서가 아니라 애플리케이션이 정의한 프레임 순서를 보장하는 장치입니다.
한 연결의 송신 소유자는 큐를 비우는 동안 새 생산자가 들어왔는지 다시 확인해야 합니다. 큐가 비었다고 writable 감시나 송신 상태를 해제한 순간 새 프레임이 들어오면 영원히 제출되지 않는 경합이 생길 수 있기 때문입니다. 이를 원자적 상태 전이와 재확인으로 해결하고, 송신 완료 후 다음 프레임 제출의 책임을 한 경로에 두겠습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- 메시지 생성·WSASend 제출·완료 처리의 세 순서를 구분한다.
- 연결별 송신 큐와 단일 제출 소유권으로 논리 프레임 혼합을 방지한다.
- 부분 전송·OVERLAPPED 버퍼 수명·TCP ACK와 애플리케이션 ACK를 나눈다.
- 느린 상대의 큐 상한과 중요도별 백프레셔·폐기 정책을 제시한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- 스레드 안전한 WSASend 호출이면 논리 메시지도 원자적으로 전송된다고 말한다.
- 완료 순서가 제출 순서와 항상 같다고 가정한다.
- 느린 상대에게 버퍼만 계속 늘려 메모리 고갈을 방치한다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- WSASend의 WSABUF 배열과 payload·OVERLAPPED는 같은 수명인가요? 호출 반환 뒤 무엇을 유지해야 하나요?
- 로컬 송신 완료를 받았습니다. 상대 애플리케이션이 메시지를 처리했음을 확인하려면 어떤 ACK와 상태가 필요한가요?
- 재접속 시 미확인 프레임을 메시지 ID·버전·멱등 처리와 어떻게 결합할까요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.