비동기 입출력 수행.
동기IO함수 > 비동기IO 함수
accept > AcceptEx
connect > ConnectEx
send > WSASend
sendto > WSASendTo
recv > WSARecv
recvfrom > WSARecvFrom
(#include <Mswsock.h>, #pragma comment(lib, "Mswsock.lib") 필요)
**.비동기 IO함수의 OVERLAPPED 구조체 포인터 인자에 NULL 전달시 동기 IO함수로 사용할 수 있다.
**.비동기 IO 함수는 내부에서 ResetEvent()를 호출한다.
BOOL AcceptEx(SOCKET sListenSocket, SOCKET sAcceptSocket, PVOID lpOutputBuffer,
DWORD dwReceiveDataLength, DWORD dwLocalAddressLength,
DWORD dwREmoteAddressLength,
LPWORD lpdwBytesReceive, LPOVERLAPPED lpOverlapped);
/*
1st : 대기 소켓
2nd : accept한 소켓(accept()의 리턴 값과 같음)
3rd : 클라이언트의 첫 데이터를 받을 버퍼 주소.
버퍼크기 = (sizeof(struct sockaddr_in)+16)*2 + 4nd인자
4th : 3rd의 byte 길이
5th : 2nd를 통해 얻은 (로컬 주소 + 16) 값
6th : 2nd를 통해 얻은 (리모트 주소 + 16) 값
7th : 미사용
8th : WSAOVERLAPPED 구조체. hEvent 외엔 모두 0으로 세팅하여 넘겨주도록 한다.
typedef struct _OVERLAPPED{
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
return : 성공-TRUE, 실패-FALSE(비동기이므로 거의 FALSE반환)
*/
void GetAcceptExSockeaddrs(PVOID lpOutputBuffer, DWORD dwReceiveDataLength,
DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength,
LPSOCKADDR* LocalSockaddr, LPINT LocalSockaddrLength,
LPSOCKADDR* RemoteSockaddr, LPINT RemoteSockaddrLength);
접속자의 주소 정보를 얻기 위한 함수.
AcceptEx의 3nd 인자인 PVOID lpOutputBuffer를 분석함.
/*
1st : AcceptEx의 3nd 인자
2nd : AcceptEx의 4th 인자
3rd : AcceptEx의 5th 인자
4th : AcceptEx의 6th 인자
5th :
6th :
7th :
8th :
*/
**. WSASend, WSARecv 함수로 전달되는 버퍼는 절대 입출력이 완료될때까지 사라지면 안된다.
int WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionroutine);
/*
1st : 수신할 소켓
2nd : 수신받을 버퍼
typedef struct __WSABUF{
u_long len;
char FAR* buf;
}WSABUF, *LPWSABUF;
**. 분할 버퍼:scatter-gather 기능
WSABUF wsabuf[3];
wsaBuf[0].buf = &header;
wsaBuf[0].len = sizeof(header);
wsaBuf[1].buf = &body;
wsaBuf[1].len = sizeof(body);
wsaBuf[2].buf = &trailer;
wsaBuf[2].len = sizeof(trailer);
WSARecv(s, wsaBuf, 3, ...);
3rd : 2nd 인자의 배열 개수
4th : 실제 수신된 데이터 크기
5th : flag(MSG_OOB 등..)의 포인터
6th, 7th :
case 1 - 비동기 IO 결과를 이벤트 신호로 통보 받기
6th : hEvent에 이벤트가 지정된 WSAOVERLAPPED 구조체 주소 전달.
case 2 -
6th : hEvent가 NULL인 WSAOVERLAPPED 구조체 주소 전달.
7th : APC(Asynchronous Procedure Call) 루틴 지정 (Completion Routine)
(IO 종료 후 윈도우즈는 APC 루틴을 호출함)
return : 성공-zero, 실패-SOCKET_ERROR(-1)
SOCKET_ERROR이고 WSAGetLastError()가 WSA_IO_PENDING인 경우 비동기 IO 실행을 의미
EX) int ret = WSARecv(..);
if (ret == SOCKET_ERROR && WSAGetLastError()!= WSA_IO_PENDING)
//error
WSAWaitForMultipleEvents(..);
*/
int WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
인자 : WSARecv() 와 동일
BOOL WSAGetOverlappedResult(SOCKET s, LPWSAOVERLAPPED lpOverlapped,
LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags);
IO 결과의 byte 크기를 얻고 IO 완료를 대기한다.
/*
1st : IO 결과를 얻을 소켓
2nd : WSARecv/WSASend에서 사용한 WSAOVERLAPPED 구조체
3rd : IO 완료된 bytes 수를 얻기 위한 DWORD 변수 포인터
4th : IO 완료를 대기할지 여부(FALSE-비대기, TRUE-대기)
보통 WSAWaitForMultipleEvents()로 대기하고 내려오기때문에 FALSE 사용.
5th : WSARecv/WSASend의 5th인자인 플래그
return : 성공-TRUE, 실패-FALSE. 4th 인자가 FALSE(비대기)인 경우 대부분 TRUE로 즉시 리턴
*/
Completion Routine
WSASend/WSARecv 호출시 마지막 인자로 지정하며 비동기 IO 완료시점에 호출됨.
void CALLBACK CompletionROUTINE(
DWORD dwError,
DWORD CbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags
);
WSAWaitForMultipleEvents()의 마지막 인자를 TRUE로 주도록 한다.