반응형

iocp서버 공부를 하던 중 클라가 접속했을 때 정상적으로 큐에 반응은 오나

구조체 OverlappedEx라는 멤버변수의 값이 변경되는 버그가 발생했고

 

왜 발생하였는가와 어떻게 해결했는가를 공유하려합니다.

 

OverlappedEx 구조체


enum CompletionType
{
	NONE,
	RECV,
	SEND,
	CONNECT,
	DISCONNECT,
};

struct OverlappedEx
{
	LPOVERLAPPED overlapped = {};
	CompletionType type = NONE;
	char _buf[BUF_SIZE] = {};
};

OverlappedEx의 구조체입니다.

멤버 변수로는 overlapped와 type, buf가 있습니다.

 

문제점

unsigned __stdcall IOCP::AcceptThread(void* args)
{
	
    ...
    
    OverlappedEx* overlapped = new OverlappedEx();
    overlapped->type = CONNECT;
	if (acceptEx(_servSocket, _clientSocket, overlapped->_buf, 0,
		sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16,
		&bytes, (LPOVERLAPPED)overlapped) == FALSE) {
		if (WSAGetLastError() != WSA_IO_PENDING) {
			std::cerr << "AcceptEx failed: " << WSAGetLastError() << std::endl;
			//closesocket(clientSock);
			delete overlapped;
		}
	}
	return 0;
}
unsigned __stdcall IOCP::WorkerThread(void* args)
{
	Session* session = nullptr;
	OverlappedEx* overlapped = nullptr;
	DWORD transferredBytes = 0;

	while (true)
	{
		bool ret = GetQueuedCompletionStatus(_iocpHandle, &transferredBytes, (PULONG_PTR)&session, (LPOVERLAPPED*)(&overlapped), INFINITE);

		if (ret == true)
		{
			std::cout << "transferredBytes : " << transferredBytes << std::endl;
			switch (overlapped->type)
			{
			case RECV:
				std::cout << "onrecv : " << session->_buf << std::endl;
				break;
			case SEND:
				break;
			case CONNECT:
				std::cout << "connected\n";
				break;
			...
		}
	}
}

보시면 AcceptEx를 호출하기 전 분명히 type을 CONNECT로 초기화를 했는데도

큐에서 받아온 OverlappedEx의 type은 NONE으로 설정이 되어있던것

 

이때문에 acceptEx로 접속을 수락해도 WorkerThread에서는 switch문을 제대로 탈 수 없었습니다.

 

원인발견 및 해결방법

결론부터 말씀드리자면 변수 타입문제였습니다.

OverlappedEx의 overlapped 자료형은 LPOVERLAPPED로 되어있었습니다.

이게 왜 문제였냐면 _OVERLAPPED 의 별칭은 OVERLAPPED와 *LPOVERLAPPED가 있습니다.

후자는 *로서 8바이트의 크기를 가지고 전자는 32바이트의 크기를 가집니다.

 

각 상황을 디스어셈블리로 표현한걸 캡쳐했습니다.

 

위쪽 이미지가 LPOVERLAPPED 자료형인 경우

아래쪽이 OVERLAPPED 자료형인 경우입니다.

 

보시면 각 이미지의 두 번째 라인에서 차이점이 보입니다.

 

해석을 하자면

[rax+8] 위치에 3을 대입, [rax+20h] 위치에 3을 대입

 

이런식으로 대입하는 주소의 위치 차이가 나기때문에 실제 큐에서 받아온 OverlappedEx의 type이 항상 이상한값으로 채워져있던 것이었죠

 

따라서 자료형을 LPOVERLAPPED → OVERLAPPED로 바꾸었더니 해결되었습니다!

 

반응형
원피스는 실존하다