공부

게임서버 프로그래밍 #2 (소켓 프로그래밍)

한영인 2022. 7. 6. 04:29

네트워크 프로그래밍 단계

 

Client

소켓 생성(Socket) -> 서버 소켓 연결(Connect) -> Data /수신(Send/Recv) -> 소켓 끊기(Close)

 

Server

소켓 생성 -> 소켓 묶기 -> 소켓 접속 대기 -> 연결 소켓 생성 -> Data /수신 -> 소켓 끊기

Socket -> Bind -> Listen -> Accept -> Recv/Send -> Close

 

클라이언트와 서버가 통신하기 위하여 필요한 과정

 

Socket 생성 (SOCKET)

파라미터에 맞는 소켓을 생성해주는 함수이다.

SOCKET WSASocket(int af, int type, int protocol, LPWSAPROTOCOL_INFO IpProtocolInfo, GROUP g, DWORD dwFlags)

이 함수의 인수

       af: address family. 보통 AF_INET <- IPv4를 사용할 때 사용하는 상수 IPv6는 AF_INET6을 사용한다.

       type: 소켓의 타입. 보통은 tcp SOCKET_STREAM udpSOCK_DGRAM

       protocol: 사용할 프로토콜 종류. 보통은 IPPROTO_TCP or IPPROTO_UDP

       IpProtocolInfo: 프로토콜의 정보. 보통은 NULL값으로 둔다.

       dwFlags: 소켓의 속성. 보통은 사용하지 않기에 0으로 두는데, OVERLAPPED_IO IOCP를사용할때에는 사용하게                          된다. 그때에는 WSA_PROTOCOL_OVERLAPPED를 사용한다.

 

소켓의 연결 (Connect)

대기하고 있는 상대방의 소켓에 자신의 소켓을 연결하는 함수

int WSAConnect(SOCKET s, const struct sockaddr* name, int namelen, LPWSABUF IpCallerData, LPWSABUF IpCalleeData, LPQOS IpSQOS, LPQOS IpGQOS)

이 함수의 인수

       s: WSASocket에서 만든 소켓을 넣어준다.

       Name: 소켓의 주소를 담는 포인터

       Namelen: 소켓 주소의 길이

       IpCallerData, IpCalleeData: 접속도중 상대방에서 송/수신 받을 유저 데이터의 포인터, 수업때 다루진 않음.

       LPQOS : 안알려주셔서 찾아봤는데 QOSQuality Of Service의 약자로, 어플리케이션의 동작에 따라 네트워크

       디바이스가 트래픽을 조작하는 것 이라는데 이것도 안쓰임. 넘어가

 

Socket에서 데이터 받기 (RECV)

WSARecv를 통해 받는다. 여기서 왜 Recv가 아니냐?

-> 여러가지 고성능 API를 사용하기 위해서 Recv에서 확장된 WSARecv를 사용한다.

int WSARecv(SOCKET s, LPWSABUF IpBuffers, DWORD dwBufferCount, LPDWORD IpNumberofBytesRecvd, LPDWORD IpFlags, LPWSAOVERLAPPED IpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE IpComletionRoutine)

이 함수의 인수

       s: 소켓

       IpBuffers: Recv한 데이터를 저장하는 버퍼.

       dwBufferCount: 버퍼의 개수

       여기서 버퍼가 담을 수 있는 데이터의 크기는 구조체 안에 있음

       IpNumberofBytesRecvd: 내가 몇Byte를 Recv했는지 모르기 때문에 여기서 값을 전달받음

       IpFlags: MSG_PEEK(데이터 존재 유무 확인), MSG_OOB(우선도착) 등등의 옵션?을 넣어줌. 강의에서는 잘 사용 X

       IpOverlappedIpComletionRoutine는 뒤 게시물에서 정리하여 올리도록 하겠습니당

 

여기서 WSABUF?
흩어져있는 값들을 관리하는 구조 버퍼를 여러개를 둘 수 있게 하기 위해서 사용한다 하심

예를들어 패킷 세개를 보낼 때 패킷 세개를 하나의 버퍼에 copy해서 한꺼번에 보내게 된다면 메모리 복사를 해야하기 때문에 OverHead가 굉장히 큼.

따라서 패킷 세개에 대한 주소값과 크기만을 담은 값을 보내게 된다면 성능향상에 도움이 된다.

이 기술은scatter gather I/O라고 하는데,  boost/asio에서도 적용된다고 한다.

정확한건 마소 독스 >> https://docs.microsoft.com/ko-kr/windows/win32/winsock/scatter-gather-i-o-2

이런식으로 buf와 len에는 각각 주소값, 그리고 버퍼의 크기를 나타냄

소켓 묶기 (BIND)

포트를 선점하여 다른 프로그램이 사용하지 못하도록 하는 함수

int bind(SOCKET s, const struct sockaddr* name, int namelen)

name과 namelen은 상대 소켓의 주소와, 길이를 나타내는데, 왠만해서는 모든 주소로부터 접속을 허용하잔슴? 그때는

INADDR_ANY모든 주소로부터 접속을 허용시킴. 다른 국가, IP만 막고싶은건 라우터를 통해 하는거. 우리는 할일이 많다!

 

소켓 접속 대기 (LISTEN)

소켓이 접속을 받을 수 있도록 만드는 함수

int listen(SOCKET s, int backlog)

Backlog: accept전 접속 대기 큐의 최대 연결가능숫자. 대용량게임서버에선 SOMAXCONN값을 주로 넣음

 

연결 소켓 생성 (ACCEPT)

연결된 소켓을 만드는 함수

SOCKET WSAAccept(SOCKET s, struct sockaddr* name, LPINT addrlen, LPCONDITIONPROC IpfnCondition, DWORD dwCallbackData)

이 함수의 인자

       s: Accept할때만 사용하는, 현재 listen을 하고있는 소켓.

       addrlen: 클라이언트에 연결하면 어떤 클라이언트에 연결했나 클라이언트의 주소를 넣어줌.

       중간에 접속이 끊어졌다 재접속했는지 확인해야하고, IP를 밴했다면 그것도 확인해야 함.

       IpfnCondition, dwCallbackData: WSA로 확장하며 생김. Accept를 하기 전, 주소가 내가 받아도 되는지 판단을 하는

       파라미터. 이전에 특정 IP나 특정 나라를 차단할 때 쓰라고 만들었지만, 서버가 이걸 하고있을 여유가 없다

       그래서 라우터단에서 해줌

       WSAAccept에서 리턴한 SOCKET : 이 클라이언트와 1대1로 연결되는 소켓. send/recv할때는 이 소켓을 사용해야 함.

 

 

위 게시물은 현 한국공학대학교 게임공학과 교수님이신 정내훈 교수님의 강의를 듣고 정리한 내용입니다.

'공부' 카테고리의 다른 글

게임서버 프로그래밍 #1 (네트워크의 기초)  (0) 2022.07.05
간단한 C 포인터 문제  (0) 2022.06.11