Загружаем каталог…
Загружаем каталог…
Overlapped 확장 구조체 IOCP에서 WSARecv()나 WSASend()를 비동기로 호출하면 함수는 I/O가 끝날 때까지 기다리지 않고 바로 반환된다. 그래서 나중에 I/O가 완료되었을 때, 이 작업이 Recv였는지 Send였는지 어떤 버퍼를 사용했는지 어떤 작업의 완료인지 를 다시 알아낼 방법이 필요하다. Windows API(WSARecv, GetQueuedCompletionStatus)는 WSAOVERLAPPED 구조체의 주소만 안다. Windows에서는 이를 위해 WSAOVERLAPPED 구조체를 사용한다. typedef struct _WSAOVERLAPPED { ULONG_PTR Internal; ULONG_PTR InternalHigh; union { struct { DWORD Offset; DWORD OffsetHigh; }; PVOID Pointer; }; WSAEVENT hEvent; } WSAOVERLAPPED; 하지만 WSAOVERLAPPED 자체에는 우리가 필요한 Recv/Send 작업 종류, 버퍼 같은 정보가 없다. 그래서 실제 IOCP 서버에서는 WSAOVERLAPPED를 포함하는 별도의 구조체를 만들어 사용하는 경우가 많다. enum IO_OPERATION { IO_RECV, IO_SEND, IO_ACCEPT }; struct OverlappedContext { // 반드시 첫 번째 멤버로 배치 WSAOVERLAPPED overlapped; // 개발자가 필요한 추가 정보 IO_OPERATION operation; WSABUF wsaBuf; char buffer[1024]; }; 왜 WSAOVERLAPPED를 첫 번째 멤버로 둘까? OverlappedContext의 시작 주소(0x1000)와 첫 번째 멤버인 overlapped의 시작 주소(0x1000)는 일치한다. OS에 넘길 때는 &overlapped(0x1000)를 넘겨주고 나중에 OS에서 돌려받을 때는 OverlappedContext*)0x1000으로 형변환(Casting)해서 내부의 변수 데이터들을 불러오는 방식을 사용한다. 코드 struct IocpEvent // 또는 OverlappedEx { WSAOVERLAPPED overlapped = {}; // [0바이트 위치] 반드시 맨 앞에 둔다 EventType eventType; // Recv / Send 등의 구분값 std::shared_ptr<IocpObject> owner; // 이 작업을 요청한 주인(세션) }; OverlappedContext 와 같은 확장 구조체이다. 코드를 보면 owner 라는 변수가 있다. 비동기 작업은 요청한 시점과 완료되는 시점에 차이가 있다. 왜 shared_ptr<IocpObject> owner 를 보관할까? 만약 유저가 비동기 수신(WSARecv) 중에 갑자기 랜선을 뽑아버리고 메인 스레드가 해당 세션을 메모리에서 delete해 버린다면? 뒤늦게 완료 통지가 왔을 때 워커 스레드는 이미 삭제된 세션 메모리에 접근하여 서버가 크래시(Crash) 발생한다. void Session::RegisterRecv() { // 1. OVERLAPPED 메모리를 깨끗하게 0으로 초기화 _recvEvent.Init(); // 2. 비동기 작업이 끝날 때까지 내 자신이 파괴되지 않도록 소유권 보관 (Ref Count +1) _recvEvent.owner = shared_from_this(); WSABUF wsaBuf; wsaBuf.buf = _recvBuffer; wsaBuf.len = sizeof(_recvBuffer); DWORD numOfBytes = 0; DWORD flags = 0; // 3. OS에게 비동기 수신 요청! (내 확장 구조체의 맨 앞 주소를 넘김) ::WSARecv(_socket, &wsaBuf, 1, &numOfBytes, &flags, &_recvEvent.overlapped, nullptr); } 이를 막기 위해 비동기 요청을 걸 때 owner = shared_from_this()로 참조 카운트를 1 올려서 OS 작업이 끝날 때 까지 객체가 죽지 않게 붙잡아 둔다. 전체 동작 흐름 정리 IOCP에서 OVERLAPPED는 단순히 비동기 작업 하나를 식별하는 중요한 단서가 된다. 하지만 기본 OVERLAPPED만으로는 해당 작업이 Recv, Send, Accept 중 무엇인지, 어떤 객체가 요청한 작업인지 알기 어렵다. 따라서 실제 서버에서는 OVERLAPPED를 포함하는 확장 구조체를 만들어 추가 정보를 함께 관리한다. IocpEvent ├─ WSAOVERLAPPED → Windows가 사용 ├─ EventType → 어떤 I/O인지 구분 └─ owner → 작업을 요청한 객체 관리 비동기 작업을 요청할 때는 OVERLAPPED 만 OS에 전달하고, 완료 시 돌려받은 OVERLAPPED 를 다시 IocpEvent*로 해석하여 필요한 정보를 복구한다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
[C++ 서버] Overlapped 확장 구조체와 비동기 객체 생명주기 관리. Overlapped 확장 구조체 IOCP에서 WSARecv()나 WSASend()를 비동기로 호출하면 함수는 I/O가 끝날 때까지 기다리지 않고 바로 반환된다. 그래서 나중에 I/O가 완료되었을 때, 이 작업이 Recv였는지 Send였는지 어떤 버퍼를 사용했는지 어떤 작업의 완료인지 를 다시 알아낼 방법이 필요하다. Windows API(WSARecv, GetQueuedCompletionStatus)는 WSAOVERLAPPED 구조체의 주소만 안다. Windows에서는 이를 위해 WSAOVERLAPPED 구조체를 사용한다. typedef struct _WSAOVERLAPPED { ULONG_PTR Internal;…
Открыть источник