논블로킹 소켓 WSA 함수 (TCP / UDP)
논블로킹 소켓으로 비동기 I/O를 하고 싶을 때 사용하는 함수를 정리한다.
TCP 관련
- WSAConnect
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaconnect
int WSAAPI WSAConnect(
SOCKET s,
const sockaddr *name,
int namelen,
LPWSABUF lpCallerData,
LPWSABUF lpCalleeData,
LPQOS lpSQOS,
LPQOS lpGQOS
);
성공일 때 0 아니면 SOCKET_ERROR
- s : 접속할 주소에 연결된 소켓을 받기 위한 핸들
- name : SOCKADDR 포인터 (접속 주소)
- namelen : 접속하기 위한 정보 길이
- 나머지 4개는 잘 쓰지 않는 인자이다.
예시)
WSAConnect(m_hSocket, (LPSOCKADDR)&RemoteAddressInfo, sizeof(SOCKADDR_IN), NULL, NULL, NULL, NULL)
- AcceptEx
https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
BOOL AcceptEx(
SOCKET sListenSocket,
SOCKET sAcceptSocket,
PVOID lpOutputBuffer,
DWORD dwReceiveDataLength,
DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength,
LPDWORD lpdwBytesReceived,
LPOVERLAPPED lpOverlapped
);
성공일 때 TRUE 반환
- sListenSocket : 서버와 같이 이미 listen을 호출한 소켓을 지정한다.
- sAcceptSocket : 연결되어 할당될 예정일 소켓을 지정한다. 이미 연결된 소켓을 전달하지 않는다.
- lpOutputBuffer : 서버의 로컬 주소와 클라이언트의 원격 주소 등 새로운 연결로부터 받을 수 있는 데이터에 대한 버퍼 포인터 (Accept이후 즉시 Read할 경우 사용할 버퍼)
- dwReceiveDataLength : Read를 할 때 사용할 버퍼 크기 (0일 경우 데이터를 수신 하지 않고 수락만 한다.)
- dwLocalAddressLength : 지역 주소 정보가 담기기 위해 예약된 바이트 크기
- dwRemoteAddressLength : 원격 주소 정보가 담기기 위해 예약된 바이트 크기
- lpdwBytesReceived : Read된 크기, ERROR_IO_PENDING 반환시 설정되지 않고 나중에 Read 된다.
- lpOverlapped : Accept에서 사용할 Overlapped 구조체
- AcceptEx를 사용하면 연결을 보다 빨리 할 수 있다.
- 단일 버퍼에서 데이터와 지역 소켓 주소(서버), 그리고 원격 소켓 주소(클라이언트)를 모드 얻을 수 있다.
주의사항
단일 버퍼를 통해 성능을 올리기 위해서, 단일 버퍼를 data, 서버 주소 그리고 원격 주소로 파싱하기 위해서 GetAcceptExSockAddrs 함수를 호출해야한다. AcceptEX 함수가 SO_UPDATE_ACCEPT_CONTEX 옵션이 어셉된 소켓에 지정되어 있다면 클라이언트 주소와 연관된 서버 주소를 getsockname으로 얻을 수 있고 클라이언트 주소는 getpeername으로 얻을 수 있다.
지역 주소와 원격 주소를 위한 버퍼는 주소를 담는 구조체 sockaddr_in는 16바이트 이나 내부적으로 정보를 더 담기 때문에 최소 32바이트를 할당해야한다.
- WSARecv
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsarecv
클라이언트 측과 소켓이 연결된 이후 메세지를 받기 위한 함수.
int WSAAPI WSARecv(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
성공일 때 0, 아닐 경우 SOCKET_ERROR 혹은 WSA_IO_PENDING / WSAEWOULDBLOCK
- s : 데이터를 받을 소켓
- lpBuffers : 받은 데이터를 저장할 WSABUF 버퍼
- dwBufferCount : wsaBuf 배열 개수
- lpNumberOfBytesRecvd : 동기 소켓에서 받은 데이터 크기
- lpFlags : WSARecv 행동 방식을 결정
- lpOverlapped : 사용하는 Overlapped 구조체
- lpCompletionRoutine : Callback 방식의 소켓 이벤트를 사용할 때
예시) Overlapped 구조체를 사용할 경우
WSABUF wsaBuf = {};
wsaBuf.buf = (char*)m_pReadBuffer;
wsaBuf.len = MAX_BUFFER_LENGTH;
DWORD iReadFlag = 0;
int ret = WSARecv(m_hSocket, &wsaBuf, 1, nullptr, &iReadFlag, &m_tReadOverlapped.tOverlapped, nullptr);
- WSASend
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasend
int WSAAPI WSASend(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
- s : 송신 소켓 핸들
- lpBuffers : 송신할 데이터를 담는 WSABUF 버퍼
- dwBufferCount : WSABUF 버퍼 배열
- lpNumberOfBytesSent : 전송이 완료된 이후 전송된 크기가 저장, , lpOverlapped 인자가 NULL 아니라면 이 인자를 NULL로 지정할 수 있다.
- dwFlags : WSASend 행동 방식을 결정
- lpOverlapped : WSAOVERLAPPED 구조체에 대한 포인터,
- lpCompletionRoutine : 완료 이후 실행될 루틴을 지정
UDP 관련
TCP와 달리 Listen, Connect과정이 필요 없기 때문에 해당 비동기 함수를 사용하지 않는다.
- WSARecvFrom
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsarecvfrom
int WSAAPI WSARecvFrom(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
sockaddr *lpFrom,
LPINT lpFromlen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
반환값이 0일 경우 성공
- s : 데이터를 받을 소켓
- lpBuffers : 받은 데이터를 저장할 WSABUF 버퍼
- dwBufferCount : wsaBuf 배열 개수
- lpNumberOfBytesRecvd : 작업 완전 완료 후 받은 데이터의 크기, lpOverlapped 인자가 NULL 아니라면 이 인자를 NULL로 지정할 수 있다.
- lpFlags : WSARecvFrom 행동 방식
- lpFrom : 데이터를 보낸 상대의 주소 정보
- lpOverlapped : 사용하는 Overlapped 구조체
- lpCompletionRoutine, Callback 방식의 소켓 이벤트를 사용할 때
- WSASendTo
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendto
int WSAAPI WSASendTo(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
const sockaddr *lpTo,
int iTolen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
- s : 송신측 소켓 핸들
- lpBuffers : wsaBuf 보낼 데이터를 담는 구조체
- dwBufferCount : wsaBuf 배열 개수
- lpNumberOfBytesSent : 전송 사이즈 크기, lpOverlapped 인자가 NULL 아니라면 이 인자를 NULL로 지정할 수 있다.
- dwFlag : WSASendTo 행동 방식을 결정
- lpTo : 목표 소켓에 대한 주소를 담는 구조체의 포인터
- iTolen : 목표 소켓에 대한 주소를 담는 구조체의 크기
- lpOverlapped : WSAOVERLAPPED 구조체에 대한 포인터
- lpCompletionRoutine : 송신 완료 이후 실행될 callback