int main()
{
// SocketUtils라는 전역의 클래스를 이용하여
// 아래와 같이 쓰고싶어서 만든다
SOCKET socket = SocketUtils::CreateSocket();
SocketUtils::BindAnyAddress(socket, 7777);
SocketUtils::Listen(socket);
SOCKET clientSocket = ::accept(socket, nullptr, nullptr);
cout << "Client Connected!" << endl;
while (true)
{
}
GThreadManager->Join();
}
// SocketUtils의 초기화/삭제는 아래와 같이 글로벌로 관리
class CoreGlobal
{
public:
CoreGlobal()
{
GThreadManager = new ThreadManager();
GMemory = new Memory();
GDeadLockProfiler = new DeadLockProfiler();
SocketUtils::Init();
}
~CoreGlobal()
{
delete GThreadManager;
delete GMemory;
delete GDeadLockProfiler;
SocketUtils::Clear();
}
} GCoreGlobal;
SocketUtils에서 IP Add를 관리하기 위해서 NetAddress클래스를 생성
class NetAddress
{
public:
NetAddress() = default;
NetAddress(SOCKADDR_IN sockAddr);
NetAddress(wstring ip, uint16 port);
SOCKADDR_IN& GetSockAddr() { return _sockAddr; }
wstring GetIpAddress();
uint16 GetPort() { return ::ntohs(_sockAddr.sin_port); }
public:
static IN_ADDR Ip2Address(const WCHAR* ip);
private:
SOCKADDR_IN _sockAddr = {};
};
NetAddress::NetAddress(SOCKADDR_IN sockAddr) : _sockAddr(sockAddr)
{
}
NetAddress::NetAddress(wstring ip, uint16 port)
{
::memset(&_sockAddr, 0, sizeof(_sockAddr));
_sockAddr.sin_family = AF_INET;
_sockAddr.sin_addr = Ip2Address(ip.c_str());
_sockAddr.sin_port = ::htons(port);
}
wstring NetAddress::GetIpAddress()
{
WCHAR buffer[100];
::InetNtopW(AF_INET, &_sockAddr.sin_addr, buffer, len32(buffer));
return wstring(buffer);
}
IN_ADDR NetAddress::Ip2Address(const WCHAR* ip)
{
IN_ADDR address;
::InetPtonW(AF_INET, ip, &address);
return address;
}
SocketUtils
내부구현
#pragma once
#include "NetAddress.h"
/*----------------
SocketUtils
-----------------*/
class SocketUtils
{
public:
// 함수포인터 사용의 목적은 IOCP Socket의 Connect, Disconnect, Accept의 callback(event)를 받고싶다
static LPFN_CONNECTEX ConnectEx;
static LPFN_DISCONNECTEX DisconnectEx;
static LPFN_ACCEPTEX AcceptEx;
public:
static void Init();
static void Clear();
static bool BindWindowsFunction(SOCKET socket, GUID guid, LPVOID* fn);
static SOCKET CreateSocket();
static bool SetLinger(SOCKET socket, uint16 onoff, uint16 linger);
static bool SetReuseAddress(SOCKET socket, bool flag);
static bool SetRecvBufferSize(SOCKET socket, int32 size);
static bool SetSendBufferSize(SOCKET socket, int32 size);
static bool SetTcpNoDelay(SOCKET socket, bool flag);
static bool SetUpdateAcceptSocket(SOCKET socket, SOCKET listenSocket);
static bool Bind(SOCKET socket, NetAddress netAddr);
static bool BindAnyAddress(SOCKET socket, uint16 port);
static bool Listen(SOCKET socket, int32 backlog = SOMAXCONN);
static void Close(SOCKET& socket);
};
template<typename T>
static inline bool SetSockOpt(SOCKET socket, int32 level, int32 optName, T optVal)
{
return SOCKET_ERROR != ::setsockopt(socket, level, optName, reinterpret_cast<char*>(&optVal), sizeof(T));
}
#include "pch.h"
#include "SocketUtils.h"
/*----------------
SocketUtils
-----------------*/
LPFN_CONNECTEX SocketUtils::ConnectEx = nullptr;
LPFN_DISCONNECTEX SocketUtils::DisconnectEx = nullptr;
LPFN_ACCEPTEX SocketUtils::AcceptEx = nullptr;
void SocketUtils::Init()
{
WSADATA wsaData;
ASSERT_CRASH(::WSAStartup(MAKEWORD(2, 2), OUT &wsaData) == 0);
/* 런타임에 주소 얻어오는 API */
// connect, disconnect, accept 함수주소를 받아온다
SOCKET dummySocket = CreateSocket();
ASSERT_CRASH(BindWindowsFunction(dummySocket, WSAID_CONNECTEX, reinterpret_cast<LPVOID*>(&ConnectEx)));
ASSERT_CRASH(BindWindowsFunction(dummySocket, WSAID_DISCONNECTEX, reinterpret_cast<LPVOID*>(&DisconnectEx)));
ASSERT_CRASH(BindWindowsFunction(dummySocket, WSAID_ACCEPTEX, reinterpret_cast<LPVOID*>(&AcceptEx)));
Close(dummySocket);
}
void SocketUtils::Clear()
{
::WSACleanup();
}
bool SocketUtils::BindWindowsFunction(SOCKET socket, GUID guid, LPVOID* fn)
{
DWORD bytes = 0;
return SOCKET_ERROR != ::WSAIoctl(socket, // 임시로 더미소켓을 사용함.
SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid), fn, sizeof(*fn), OUT & bytes, NULL, NULL);
}
SOCKET SocketUtils::CreateSocket()
{
return ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
}
bool SocketUtils::SetLinger(SOCKET socket, uint16 onoff, uint16 linger)
{
LINGER option;
option.l_onoff = onoff;
option.l_linger = linger;
return SetSockOpt(socket, SOL_SOCKET, SO_LINGER, option);
}
bool SocketUtils::SetReuseAddress(SOCKET socket, bool flag)
{
return SetSockOpt(socket, SOL_SOCKET, SO_REUSEADDR, flag);
}
bool SocketUtils::SetRecvBufferSize(SOCKET socket, int32 size)
{
return SetSockOpt(socket, SOL_SOCKET, SO_RCVBUF, size);
}
bool SocketUtils::SetSendBufferSize(SOCKET socket, int32 size)
{
return SetSockOpt(socket, SOL_SOCKET, SO_SNDBUF, size);
}
bool SocketUtils::SetTcpNoDelay(SOCKET socket, bool flag)
{
return SetSockOpt(socket, SOL_SOCKET, TCP_NODELAY, flag);
}
// ListenSocket의 특성을 ClientSocket에 그대로 적용
bool SocketUtils::SetUpdateAcceptSocket(SOCKET socket, SOCKET listenSocket)
{
return SetSockOpt(socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, listenSocket);
}
bool SocketUtils::Bind(SOCKET socket, NetAddress netAddr)
{
return SOCKET_ERROR != ::bind(socket, reinterpret_cast<const SOCKADDR*>(&netAddr.GetSockAddr()), sizeof(SOCKADDR_IN));
}
bool SocketUtils::BindAnyAddress(SOCKET socket, uint16 port)
{
SOCKADDR_IN myAddress;
myAddress.sin_family = AF_INET;
myAddress.sin_addr.s_addr = ::htonl(INADDR_ANY);
myAddress.sin_port = ::htons(port);
return SOCKET_ERROR != ::bind(socket, reinterpret_cast<const SOCKADDR*>(&myAddress), sizeof(myAddress));
}
bool SocketUtils::Listen(SOCKET socket, int32 backlog)
{
return SOCKET_ERROR != ::listen(socket, backlog);
}
void SocketUtils::Close(SOCKET& socket)
{
if (socket != INVALID_SOCKET)
::closesocket(socket);
socket = INVALID_SOCKET;
}