우선 통신방법을 간단히 정리해 보자면.
- Client
- 소켓 준비
- 서버로 Connect
- 소켓을 통해 서버와 패킷 송수신
- Server
- Listener 소켓 준비
- Bind(서버 주소/Port 소켓과 연동)
- Listen
- Accept
- 소켓을 통해 Client와 통신
소켓프로그래밍을 하기위한 기본 include
// ...
#include <winsock2.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
// ...
클라이언트
클라이언트를 먼저 구성해보자
int main()
{
// 윈속 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), // 2.2 버전 써달라
&wsaData) != 0) // wsaData에 정보 받음
return 0;
// 윈속을 초기화 후 ::WSACleanup(); 종료까지 불러줘야함.
// ad : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0(프로토콜은 알아서 선택해 달라)
// return : descriptor
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0); // 소켓을 만들어 달라
if (clientSocket == INVALID_SOCKET)
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 연결할 목적지는? (IP주소 + Port) -> XX 아파트 YY 호
SOCKADDR_IN serverAddr; // IPv4
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
//serverAddr.sin_addr.s_addr = ::inet_addr("127.0.0.1"); << deprecated(구식이니 사용하지 말아주세요.)
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777); // 80 : HTTP
// htons(host to network short) : Endian이슈를 해결
// Little-Endian vs Big-Endian
// ex) 0x12345678 4바이트 정수
// low [0x78][0x56][0x34][0x12] high <- little = PC
// low [0x12][0x34][0x56][0x78] high <- big = network
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Connect ErrorCode : " << errCode << endl;
return 0;
}
// ---------------------------
// 연결 성공! 이제부터 데이터 송수신 가능!
cout << "Connected To Server!" << endl;
while (true)
{
// TODO
this_thread::sleep_for(1s);
}
// ---------------------------
// 소켓 리소스 반환
::closesocket(clientSocket);
// 윈속 종료
::WSACleanup();
}
클라완성
서버
int main()
{
// 윈속 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// ad : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0
// return : descriptor
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 나의 주소는? (IP주소 + Port)->XX 아파트 YY 호
SOCKADDR_IN serverAddr; // IPv4
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = ::htonl(INADDR_ANY); //< 니가 알아서 해줘
serverAddr.sin_port = ::htons(7777); // 80 : HTTP
// 안내원 폰 개통! 식당의 대표 번호
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) // 10 은 대기열의 의미, 테스트해서 적당한 값을 넣어야한다
{
int32 errCode = ::WSAGetLastError();
cout << "Listen ErrorCode : " << errCode << endl;
return 0;
}
// -----------------------------
while (true)
{
SOCKADDR_IN clientAddr; // IPv4
::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
}
// -----------------------------
// 윈속 종료
::WSACleanup();
}