Files
Sun1602/Server/Network/Acceptor.cpp
T
2022-10-26 12:25:11 +08:00

201 lines
5.8 KiB
C++

#include "stdafx.h"
#include "Acceptor.h"
#include "IoHandler.h"
#include "IOCPServer.h"
#include "Session.h"
#include "RecvBuffer.h"
#include "SessionList.h"
#include "SessionPool.h"
#include "MsWinsockUtil.h"
Acceptor::Acceptor()
{
listen_socket_ = INVALID_SOCKET;
iocp_handle_ = INVALID_HANDLE_VALUE;
for (DWORD i = 0; i < MAX_ACCEPT_THREAD; ++i) {
thread_handles_[i] = INVALID_HANDLE_VALUE;
}
number_of_threads_ = 0;
}
Acceptor::~Acceptor()
{
Shutdown();
if (iocp_handle_ != INVALID_HANDLE_VALUE) {
CloseHandle(iocp_handle_);
}
for (DWORD i = 0; i < number_of_threads_; ++i)
{
if (thread_handles_[i] != INVALID_HANDLE_VALUE) {
CloseHandle(thread_handles_[i]);
}
}
}
void Acceptor::Init(IoHandler* io_handler, DWORD number_of_threads)
{
assert(number_of_threads <= MAX_ACCEPT_THREAD);
io_handler_ = io_handler;
number_of_threads_ = number_of_threads;
// Accept용 IOCP 생성
iocp_handle_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
// 워커 쓰레드 생성
unsigned int thread_id;
for (DWORD i = 0; i < number_of_threads_; ++i)
{
thread_handles_[i] = \
(HANDLE)_beginthreadex(NULL, 0, accept_thread,
(LPVOID)this, 0/*CREATE_SUSPENDED*/, &thread_id);
}
// backlog가 다 찼을 때는 IOCP로 메시지를 받을 수 없으므로 이벤트 기반 리슨을 추가로 붙여 줘야 한다
}
//==================================================================================================
/**
@remarks
포트를 초기화한다.
@param ip_string
접속할 IP
@param port
접속할 포트
@retval BOOL
리슨에 실패하면 false 리턴
*/
//==================================================================================================
BOOL Acceptor::StartListen(const char* ip_string, WORD port)
{
// 멈춰 있으면 다시 구동시킨다.
ResumeListenThread();
// 리슨 소켓 생성
if (!CreateListenSocket(ip_string, port)) {
return false;
}
// IOCP에 리슨 소켓 등록
CreateIoCompletionPort((HANDLE)listen_socket_, iocp_handle_, 1, 0);
//Dll에서 함수 로드(AcceptEx, TransmitFile)
MsWinsockUtil::LoadExtensionFunction(listen_socket_);
return true;
}
//==================================================================================================
/**
@remarks
리슨 소켓을 생성하고 주소를 설정한다.
@retval BOOL
리슨 소켓 생성에 실패하면 false를 리턴한다.
(IP와 port가 겹치는 경우일 것이다.)
*/
//==================================================================================================
BOOL Acceptor::CreateListenSocket(const char* ip_string, WORD port)
{
// 이미 리슨 소켓이 생성되어 있으면 실패
if (listen_socket_ != INVALID_SOCKET) {
return false;
}
// 리슨 소켓 생성
listen_socket_ = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (listen_socket_ == INVALID_SOCKET) {
return false;
}
// // IO_PENDING중인 놈을 버림으로써 소켓 처리 속도를 높인다.
// // 이것을 위해 linger 옵션 적용
// LINGER slinger;
//
// slinger.l_linger = 5000;
// slinger.l_onoff = (USHORT)SO_LINGER;
// int ret = setsockopt(listen_socket_, SOL_SOCKET, SO_LINGER, (char*)&slinger, sizeof(LINGER));
#ifdef _WHATTHEHELL_NETWORK_20060519
int nFlag;
int ret = setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR, (char*)&nFlag, sizeof(nFlag));
#endif
// set a network address
ZeroMemory(&sockaddr_in_, sizeof(sockaddr_in_));
sockaddr_in_.sin_family = AF_INET;
sockaddr_in_.sin_addr.s_addr = (ip_string == NULL || strlen(ip_string) == 0)
? htonl(INADDR_ANY) : inet_addr(ip_string);
sockaddr_in_.sin_port = htons(port);
// bind
int err = ::bind(listen_socket_, (SOCKADDR *)&sockaddr_in_, sizeof(sockaddr_in_));
if (err == SOCKET_ERROR) {
::closesocket(listen_socket_);
return false;
}
// listen
err = ::listen(listen_socket_, SOMAXCONN);
if (err == SOCKET_ERROR) {
::closesocket(listen_socket_);
return false;
}
return true;
}
//==================================================================================================
/**
@remarks
리슨 쓰레드를 잠시 멈춘다.
*/
//==================================================================================================
void Acceptor::SuspendListenThread()
{
for (DWORD i = 0; i < number_of_threads_; ++i)
{
assert(thread_handles_[i] != INVALID_HANDLE_VALUE);
SuspendThread(thread_handles_[i]);
}
}
//==================================================================================================
/**
@remarks
리슨 쓰레드를 다시 구동시킨다.
*/
//==================================================================================================
void Acceptor::ResumeListenThread()
{
for (DWORD i = 0; i < number_of_threads_; ++i)
{
assert(thread_handles_ != INVALID_HANDLE_VALUE);
ResumeThread(thread_handles_[i]);
}
}
//==================================================================================================
/**
@remarks
리슨 쓰레드를 종료시킨다.
*/
//==================================================================================================
void Acceptor::Shutdown()
{
// 멈춰 있으면 다시 구동시킨다.
ResumeListenThread();
// 리슨 소켓 종료
if (listen_socket_ != INVALID_SOCKET)
{
::closesocket(listen_socket_);
listen_socket_ = INVALID_SOCKET;
}
// acceptor 쓰레드 종료
for (DWORD i = 0; i < number_of_threads_; ++i) {
PostQueuedCompletionStatus(iocp_handle_, 0, 0, NULL);
}
// 모든 워커 쓰레드가 종료될 때까지 기다린다.
WaitForMultipleObjects(number_of_threads_, thread_handles_, true, INFINITE);
}