Computer Science 기본 지식/소켓 프로그래밍

논블로킹 소켓 WSA 함수 (TCP / UDP)

로파이 2021. 8. 21. 22:41

논블로킹 소켓으로 비동기 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