Socket Programming
카테고리: Server
인프런에 있는 루키스님의 게임 서버 강의를 듣고 정리한 내용입니다.
Client Side
1. Socket 초기화
#include <winsock2.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// ...
::WSACleanup();
- Window Socket API 초기화
Startup
함수로 wsaData의 버전과 Descriptor 명시- return 값으로 성공 여부 확인 가능
- 사용이 끝나면
WSACleanUp
함수로 소켓 종료
2. Client Socket 생성
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET)
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// ...
::closesocket(clientSocket);
- af : Address Family (AF_INET = IPv4, AF_INET^ = IPv6)
- type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
- protocol : 0
- 사용 후
closesocket
으로 소켓 리소스 반환
3. Socket 연결
// 소켓에 주소 연결
SOCKADDR_IN serverAddr;
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777);
// host to network short
// Little-Endian vs Big-Endian###
// ex) 0x12345678 4바이트 정수
// low [0x78][0x56][0x34][0x12] high < little
// low [0x12][0x34][0x56][0x78] high < big = network
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errorCode = ::WSAGetLastError();
cout << "Connect ErrorCode : " << errorCode << endl;
return 0;
}
// 연결 성공! 이제부터 데이터 송수신 가능
cout << "Connected To Server!" << endl;
while (true)
{
// TODO
this_thread::sleep_for(1s);
}
SOCKADDR_IN
구조체에 소켓의 목적지 성정 (IP주소 + 포트)- 이때 시스템마다 표기법 이슈(Endian)가 있어서 별도 표기법으로 사용 (라우터, 공유기, …)
- inet_pton : Internet Presentation to Network
- htons : Host to Network Short
connect
함수로 Address에 지정한 서버와 연결 요청
Server Side
Socket 초기화 & 생성
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// ...
::WSACleanup();
- 소켓 초기화, 생성은 Client와 동일
- 일반적으로 서버에서 요청을 받는 소켓을 listener(listen socket)이라 함
Socket 연결
SOCKADDR_IN serverAddr;
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = ::htons(7777);
if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Bind ErrorCode : " << errCode << endl;
return 0;
}
if (::listen(listenSocket, 10) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Listen ErrorCode : " << errCode << endl;
return 0;
}
- client와 마찬가지로
SOCKADDR_IN
을 이용해 소켓의 주소 설정- 서버에서는 목적지가 아닌 자신의 주소
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
에서 INADDR_ANY 은 모든 IP를 허용- 서버 소켓에서는
connect
가 아닌bind
함수를 통해 소켓과 주소를 결합 listen
함수를 실행하면 이때부터 수신 가능. backlog값을 통해 대기열 요청 수 설정 가능- 대기 큐가 가득 찾을 때 추가로 들어오는 연결 요청은 거부 -> 클라에서 적절한 대응
while (true)
{
SOCKADDR_IN clientAddr;
::memset(&clientAddr, 0, sizeof(clientAddr));
int32 addrLen = sizeof(clientAddr);
SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket == INVALID_SOCKET)
{
int32 errCode = ::WSAGetLastError();
cout << "Accept ErrorCode : " << errCode << endl;
return 0;
}
char ipAddress[16];
::inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress));
cout << "Client Connected! IP = " << ipAddress << endl;
// TODO
}
accept
함수를 이용해 클라에서 요청이 들어온 소켓 받을 수 있음- 특정 아이피의 차단이 필요하다면 클라 소켓의 주소를 까보면 나옴
- 표기법 문제가 마찬가지로 존재하기 때문에
inet_ntop
함수를 통해 받아와야 함
- 표기법 문제가 마찬가지로 존재하기 때문에
댓글 남기기