티스토리 뷰

EX 시리즈란?

비동기 입출력의 경우, WSARecv와 WSASend를 통해 send와 recv는 호출을 받을 수 있다. 그렇지만 원래 사용하던 connect나 disconnect, accept는 비동기 호출을 받을 수 없다. 그런 부분을 확장하기 위해 나온 API가 아래 세 가지이다.

 

※ 사용하기 위해서는 WSAIoctl을 통해 런타임에 초기화를 진행하여야 합니다.


WSAIoctl - 초기화 함수

함수 원형

위 원형에서 제일 중요한 부분은 dwIoControlCode 입니다. dwIoControlCode에는 필수적으로SIO_GET_EXTENSION_FUNCTION_POINTER 가 들어가야 합니다. 만약 들어가게 되면 lpvInBuffer에는 GUID라는 특별한 아이덴티티가 들어가게 됩니다. GUID의 종류는 아래와 같습니다.

그래서 아래 예시를 보게 되면 GUID에는 ex 종류, fn에는 ex 포인터가 들어가게 됩니다. 정확한 예제는 아래에 있습니다.

void SocketUtils::BindWindowsFunction(SOCKET socket, GUID guid, LPVOID* fn)
{
	::WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, 
            sizeof(guid), fn, sizeof(*fn), OUT & bytes, NULL, NULL);
}

AcceptEx - LPFN_ACCEPTEX

- AcceptEx는 Accept를 비동기로 받을 수 있도록 하는 API이고 Listen소켓과 미리 받을 소켓을 미리 준비해야 합니다.

여기서 중요한 부분은 sListenSocket, sAccpetSocket, lpOutputbuffer 입니다.

sListenSocket : 리슨 소켓

sAccpetSocket : 받아올 소켓

lpOutputBuffer : 서버의 로컬 주소 및 클라이언트의 원격 주소를 수신하는 버퍼에 대한 포인터로 nullptr이 되면 X

dwLocalAddressLength, dwRemoteAddressLength는 항상 16바이트 이상이어야 하기 때문에 +16을 해줘야 합니다.

 

초기화 부분

LPFN_ACCEPTEX AcceptEx;
GUID guid= WSAID_ACCEPTEX;

SOCKET dummySocket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
::WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, 
    sizeof(guid), AcceptEx, sizeof(*AcceptEx), OUT & bytes, NULL, NULL);

 

사용하는 부분

SOCKET _listenSocekt;  // ::bind, ::listen 까지 마친 소켓
SOCKET _clientSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
OVERLAPPED overlapped; // 초기화 필수
DWORD bytesReceived = 0;
char TestBuffer[256];

AcceptEx(_listenSocket, _clientSocket, &TestBuffer, 0, sizeof(SOCKADDR_IN) + 16, 
	sizeof(SOCKADDR_IN) + 16, &bytesReceived, reinterpret_cast<LPOVERLAPPED>(&overlapped));

ConnectEx - LPFN_CONNECTEX

여기서 중요한 부분은 SockAddr 입니다. 원래 connect할 때 사용하던 방식 그대로 사용하면 됩니다.

lpSendBuffer와 dwSendDataLength는 접속 후 전송할 데이터가 있으면 사용하는 것으로, 각각 포인터 위치와 길이를 넘겨주면 됩니다.

 

초기화 부분

LPFN_CONNECTEX ConnectEx;		// 차이점
GUID guid = WSAID_CONNECTEX;	// 차이점

SOCKET dummySocket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
::WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, 
    sizeof(guid), AcceptEx, sizeof(*ConnectEx), OUT & bytes, NULL, NULL);

 

사용하는 부분

SOCKET socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN sockAddr;
ZeroMemory(&sockAddr, sizeof(SOCKADDR_IN));
serverAddr.sin_family = AF_INET;
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777);

DWORD numOfBytes = 0;
OVERLAPPED overlapped;

ConnectEx(socket, reinterpret_cast<SOCKADDR*>(&sockAddr), sizeof(sockAddr), nullptr, 0, 
			&numOfBytes, reinterpret_cast<LPOVERLAPPED>(&overlapped));

DisconnectEx - LPFN_DISCONNECTEX

여기는 dwFlagsTF_REUSE_SOCKET를 넣어주면 됩나다. 만약 사용하지 않는다면 0을 넣으면 됩니다.

만약 TF_REUSE_SOCKET를 넣었다면 후에 소켓을 재 사용하게 됩니다.

 

초기화 부분

LPFN_DISCONNECTEX DisconnectEx;		// 차이점
GUID guid = WSAID_DISCONNECTEX;	// 차이점

SOCKET dummySocket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
::WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, 
    sizeof(guid), AcceptEx, sizeof(*ConnectEx), OUT & bytes, NULL, NULL);

 

사용하는 부분

SOCKET socket // 사용중인 소켓
OVERLAPPED overlapped;

DisconnectEx(socket, reinterpret_cast<LPOVERLAPPED>(&overlapped), TF_REUSE_SOCKET, 0);
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함