25. 수신 Stream.Read 반환값 오처리(정상 종료·부분 수신)
난이도 하 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: 게임 서버의 세션 수신 루프. 각 메시지는 [길이 프리픽스 4바이트]
// 뒤에 [본문 bodyLen 바이트]가 오는 형식이다. 블로킹 소켓 위의
// NetworkStream 에서 헤더와 본문을 읽어 디스패치한다.
//
// 요구사항:
// - 상대가 연결을 정상 종료(graceful close)하면 수신 루프를 끝내고 세션을
// 정리해야 한다. 죽은 연결을 붙잡고 바쁘게 도는 일이 없어야 한다.
// - 한 번의 Read 가 요청한 만큼 다 주지 않을 수 있다. 헤더/본문은 필요한
// 바이트가 모두 모일 때까지 읽어야 한다.
//
// 과제: Stream.Read 의 반환값을 어떻게 처리하고 있는지 (A)~(C) 지점을 보고,
// 어떤 입력/상황에서 잘못 동작하는지 진단한 뒤 수정하라.
using System;
using System.Net.Sockets;
public sealed class Session
{
private volatile bool _running = true;
private readonly NetworkStream _stream;
public Session(NetworkStream s) { _stream = s; }
public void ReceiveLoop()
{
var header = new byte[4];
while (_running)
{
int n = _stream.Read(header, 0, 4); // (A) 길이 프리픽스 읽기
if (n <= 0)
continue; // (A) 받은 게 없으면 다시 시도
int bodyLen = BitConverter.ToInt32(header, 0);
var body = new byte[bodyLen];
int got = _stream.Read(body, 0, bodyLen); // (B) 본문 읽기
Dispatch(body); // (C) 본문 처리
}
}
private void Dispatch(byte[] body) { /* 패킷 핸들러로 전달 */ }
public void Stop() => _running = false;
} 결함 코드 · C++
// 시나리오: 게임 서버의 세션 수신 루프 (POSIX 소켓, C++).
// 각 메시지는 [길이 프리픽스 4바이트] 뒤에 [본문 bodyLen 바이트]가 온다.
// recv() 로 헤더와 본문을 읽어 디스패치한다.
//
// 요구사항:
// - 상대가 연결을 정상 종료하면(recv 가 0 반환) 수신 루프를 끝내고 세션을
// 정리해야 한다. 죽은 연결을 붙잡고 도는 일이 없어야 한다.
// - recv 는 요청한 만큼 다 주지 않을 수 있다(부분 수신). 또 -1 반환 시
// errno 가 일시적 상황(재시도 가능)인지 치명적 오류인지 구분해야 한다.
//
// 과제: recv 의 반환값과 errno 를 어떻게 처리하는지 (A)~(C) 지점을 보고,
// 어떤 상황에서 잘못 동작하는지 진단한 뒤 수정하라.
#include <cstdint>
#include <cstddef>
#include <sys/socket.h>
class Session {
public:
explicit Session(int fd) : fd_(fd) {}
void receiveLoop() {
uint8_t header[4];
while (running_) {
ssize_t n = recv(fd_, header, 4, 0); // (A) 길이 프리픽스 읽기
if (n < 0) {
continue; // (A) 오류면 다시 시도
}
if (n == 0) {
continue; // (A) 받은 게 없으면 다시 시도
}
int32_t bodyLen = *reinterpret_cast<int32_t*>(header);
uint8_t body[4096];
ssize_t got = recv(fd_, body, bodyLen, 0); // (B) 본문 읽기
dispatch(body, bodyLen); // (C) 본문 처리
}
}
void stop() { running_ = false; }
private:
void dispatch(const uint8_t*, int32_t) { /* 핸들러로 전달 */ }
int fd_;
bool running_ = true;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.