30. 비동기 송신 버퍼의 조기 반납: in-flight 중 재사용 (C#)
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// [시나리오] 비동기 송신에서 풀 버퍼의 수명 (서버-클라)
// 고빈도 송신을 위해 송신 버퍼를 ArrayPool 에서 빌려 쓴다.
// 메시지를 버퍼에 복사한 뒤 비동기 송신(BeginSend)을 시작하고,
// 전송이 끝나면 완료 콜백(OnSent)에서 마무리한다.
// 한 연결에는 여러 게임 로직 스레드가 브로드캐스트/개별 통지를 보낼 수 있다.
//
// [요구사항]
// - 각 클라이언트는 자신에게 보낸 바이트를 정확히(온전하게) 받아야 한다.
// - 다른 연결로 보낼 데이터가 섞이거나 새어 나가면 안 된다.
//
// [과제] Connection.Send 를 검토하고, 송신이 잦거나 여러 스레드가 같은 연결로
// 동시에 보낼 때 무엇이 잘못될 수 있는지 원인과 수정안을 제시하라.
// 표시된 (A)(B) 지점에 주목하라.
using System;
using System.Buffers;
using System.Net.Sockets;
class Connection
{
private readonly Socket _socket;
public Connection(Socket s) { _socket = s; }
// 여러 스레드에서 호출되어 이 연결로 메시지를 보낸다.
public void Send(byte[] message, int len)
{
byte[] buf = ArrayPool<byte>.Shared.Rent(len); // 풀에서 대여
Buffer.BlockCopy(message, 0, buf, 0, len);
// (A)
_socket.BeginSend(buf, 0, len, SocketFlags.None, OnSent, buf);
// (B)
ArrayPool<byte>.Shared.Return(buf);
}
private void OnSent(IAsyncResult ar)
{
int sent = _socket.EndSend(ar);
// 송신 완료 처리
}
} 결함 코드 · C++
// [시나리오] 비동기(Proactor/IOCP 류) 송신에서 풀 버퍼의 수명 (서버-클라)
// 고빈도 송신을 위해 송신 버퍼를 풀에서 빌려 쓴다.
// 메시지를 버퍼에 복사한 뒤 OS 비동기 송신을 시작(postSend)하고,
// 전송이 끝나면 완료 콜백(onSent)에서 마무리한다.
// 한 연결에는 여러 게임 로직 스레드가 브로드캐스트/개별 통지를 보낼 수 있다.
//
// [요구사항]
// - 각 클라이언트는 자신에게 보낸 바이트를 정확히(온전하게) 받아야 한다.
// - 다른 연결로 보낼 데이터가 섞이거나 새어 나가면 안 된다.
//
// [과제] Connection::asyncSend 를 검토하고, 송신이 잦거나 여러 스레드가 같은
// 연결로 동시에 보낼 때 무엇이 잘못될 수 있는지 원인과 수정안을 제시하라.
// 표시된 (A)(B) 지점에 주목하라.
#include <cstdint>
#include <cstring>
#include <cstddef>
#include <functional>
struct Buffer { uint8_t data[4096]; size_t len; };
class BufferPool {
public:
Buffer* acquire(); // 풀에서 대여
void release(Buffer* b); // 풀로 반납
};
class Connection {
BufferPool& pool_;
// OS 비동기 송신 시작: buf/len 을 커널에 넘기고, 전송이 끝나면 done() 을 호출한다.
void postSend(uint8_t* buf, size_t len, std::function<void()> done);
public:
explicit Connection(BufferPool& p) : pool_(p) {}
// 여러 스레드에서 호출되어 이 연결로 메시지를 보낸다.
void asyncSend(const uint8_t* msg, size_t len) {
Buffer* buf = pool_.acquire();
std::memcpy(buf->data, msg, len);
buf->len = len;
// (A)
postSend(buf->data, len, [this, buf]() { onSent(buf); });
// (B)
pool_.release(buf);
}
void onSent(Buffer* buf) { (void)buf; /* 송신 완료 처리 */ }
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.