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

345 lines
11 KiB
C++

#include "stdafx.h"
#include <conio.h>
#include <map>
#include "IOCPServer.h"
#include "IoHandler.h"
#include "SessionList.h"
#include "Session.h"
#include "SendBuffer.h"
#include "RecvBuffer.h"
typedef std::map<DWORD, IoHandler*,
std::less<DWORD>,
module::RedirectedAllocator<IoHandler*> > IOHANDLER_MAP;
//==================================================================================================
/**
@remarks
InPimpl 맴버 선언
*/
//==================================================================================================
struct IOCPServer::InPimpl
{
HANDLE send_thread_handle_;
bool shutdown_;
IOHANDLER_MAP io_handler_table_;
};
//==================================================================================================
/**
@remarks
세션 리스트를 순회하며 보내기 버퍼에 있는 패킷들을 전송하는 쓰레드
@param param
IOCP 서버의 포인터
*/
//==================================================================================================
unsigned __stdcall send_thread(LPVOID param)
{
IOCPServer* const iocp_server = reinterpret_cast<IOCPServer*>(param);
IOCPServer::InPimpl* const iocp_impl = iocp_server->in_impl_;
while (iocp_impl->shutdown_ == false)
{
Sleep(1); // for context-switching, no busy-waiting
IOHANDLER_MAP::const_iterator it = iocp_impl->io_handler_table_.begin();
const IOHANDLER_MAP::const_iterator end = iocp_impl->io_handler_table_.end();
for (; it != end; ++it)
{
IoHandler* const io_handler = it->second;
io_handler->ProcessSend();
}
}
return 0;
}
//==================================================================================================
//==================================================================================================
//==================================================================================================
IOCPServer::IOCPServer()
: in_impl_(new InPimpl)
{
in_impl_->shutdown_ = false;
in_impl_->send_thread_handle_ = NULL;
}
IOCPServer::~IOCPServer()
{
if (!in_impl_->shutdown_) {
Shutdown();
}
if (in_impl_->send_thread_handle_) {
CloseHandle(in_impl_->send_thread_handle_);
}
delete in_impl_;
in_impl_ = NULL;
WSACleanup();
}
//==================================================================================================
/**
@remarks
서버를 초기화한다.
@param description
서버 초기화에 필요한 정보를 담은 구조체의 포인터
@retval BOOL
윈속 초기화에 실패하거나 리슨 소켓 생성에 실패하면 false를 리턴한다.
*/
//==================================================================================================
BOOL IOCPServer::Init(LPIOHANDLER_DESC description, DWORD number_of_io_handlers)
{
// 윈속 초기화
if (!InitWinsock()) {
return false;
}
for (DWORD i = 0; i < number_of_io_handlers; ++i) {
CreateIoHandler(description + i);
}
// Send 워커 쓰레드 생성
unsigned int thread_id;
in_impl_->send_thread_handle_ = \
(HANDLE)_beginthreadex(NULL, 0, &send_thread, (LPVOID)this, 0, &thread_id);
return true;
}
void IOCPServer::CreateIoHandler(LPIOHANDLER_DESC description)
{
IoHandler* const io_handler = new IoHandler;
; io_handler->Init(this, description);
in_impl_->io_handler_table_.insert(std::make_pair(io_handler->GetKey(), io_handler));
}
///---------------------------------------------------------------------------
///---------------------------------------------------------------------------
// 담덕 추가. 현재 패킷버퍼 상태를 알기위해서 추가!
///---------------------------------------------------------------------------
///---------------------------------------------------------------------------
DWORD IOCPServer::GetMaxBuffer(DWORD io_handler_key, DWORD session_index,
BOOL is_send_buffer, BOOL is_select_max_buffer)
{
IoHandler* const io_handler = GetIoHandler(io_handler_key);
if (io_handler == NULL) {
return 0;
}
//
Session* const session = io_handler->GetSession(session_index);
if (session == NULL) {
return 0;
}
//
DWORD max_buffer_size = 0;
DWORD current_used_size = 0;
if (is_send_buffer)
{
const SendBuffer* send_buffer = session->GetSendBuffer();
// lockless check
send_buffer->GetBufferSizeInfo(max_buffer_size, current_used_size);
}
else
{
const RecvBuffer* recv_buffer = session->GetRecvBuffer();
// lockless check
recv_buffer->GetBufferSizeInfo(max_buffer_size, current_used_size);
}
return is_select_max_buffer ? max_buffer_size : current_used_size;
}
void IOCPServer::Display(DWORD io_handler_key, DWORD over_boundary_length)
{
IoHandler* const io_handler = GetIoHandler(io_handler_key);
if (io_handler == NULL) {
return;
}
SessionList* const active_sessions = io_handler->GetActiveSessionList();
SESSION_LIST::const_iterator it = active_sessions->begin();
const SESSION_LIST::const_iterator end = active_sessions->end();
DWORD max_buffer_size = 0;
DWORD current_used_size = 0;
for ( ; it != end; ++it)
{
Session* session = *it;
const SendBuffer* send_buffer = session->GetSendBuffer();
// lockless check
send_buffer->GetBufferSizeInfo(max_buffer_size, current_used_size);
if (current_used_size >= over_boundary_length)
{
session->OnLogString("Session[%d] SendBuffer Size[%d]",
session->GetIndex(), current_used_size);
}
const RecvBuffer* recv_buffer = session->GetRecvBuffer();
// lockless check
recv_buffer->GetBufferSizeInfo(max_buffer_size, current_used_size);
if (current_used_size >= over_boundary_length)
{
session->OnLogString("Session[%d] RecvBuffer Size[%d]",
session->GetIndex(), current_used_size);
}
}
}
IoHandler* IOCPServer::GetIoHandler(DWORD io_handler_key)
{
const IOHANDLER_MAP& io_handler_table = in_impl_->io_handler_table_;
IOHANDLER_MAP::const_iterator found = io_handler_table.find(io_handler_key);
if (found != io_handler_table.end())
{
IoHandler* io_handler = found->second;
return io_handler;
}
return NULL;
}
void IOCPServer::Shutdown()
{
in_impl_->shutdown_ = true;
// Send 워커 쓰레드가 종료될 때까지 기다린다.
WaitForSingleObject(in_impl_->send_thread_handle_, INFINITE);
// IoHandler들을 종료시키고 포인터를 삭제한다.
IOHANDLER_MAP::const_iterator it = in_impl_->io_handler_table_.begin();
const IOHANDLER_MAP::const_iterator end = in_impl_->io_handler_table_.end();
for (; it != end; ++it)
{
IoHandler* io_handler = it->second;
; io_handler->Shutdown();
delete io_handler;
}
in_impl_->io_handler_table_.clear();
}
//==================================================================================================
/**
@remarks
Accept된 세션과 Connect된 세션들을 IOCP에 등록하면서 활성 리스트로 옮기고
모든 세션의 받기 버퍼를 검사하여 패킷을 처리하는 등의 작업을 한다.
*/
//==================================================================================================
void IOCPServer::Update()
{
IOHANDLER_MAP::const_iterator it = in_impl_->io_handler_table_.begin();
const IOHANDLER_MAP::const_iterator end = in_impl_->io_handler_table_.end();
for (; it != end; ++it)
{
IoHandler* io_handler = it->second;
; io_handler->Update();
}
}
BOOL IOCPServer::StartListen(DWORD io_handler_key, char* ip_string, WORD port)
{
IOHANDLER_MAP::const_iterator found = in_impl_->io_handler_table_.find(io_handler_key);
const bool must_be_find_io_handler = (found != in_impl_->io_handler_table_.end());
assert(must_be_find_io_handler);
if (must_be_find_io_handler == false) {
return false;
}
IoHandler* io_handler = found->second;
return io_handler->StartListen(ip_string, port);
}
//==================================================================================================
/**
@remarks
- connect 쓰레드를 사용하여 접속을 시도한다.
@param io_handler_key
- 접속을 시도하기 위한 세션을 가지고 있는 I/O 핸들러의 키
- 접속 성공 또는 실패시 OnConnect(BOOL)이 호출된다.
@param szIP
접속할 IP
@param port
접속할 포트
@retval DWORD
접속을 시도할 세션의 인덱스
*/
//==================================================================================================
DWORD IOCPServer::Connect(DWORD io_handler_key, NetworkObject* network_object,
char* ip_string, WORD port)
{
if (network_object == NULL) {
return 0;
}
IOHANDLER_MAP::const_iterator found = in_impl_->io_handler_table_.find(io_handler_key);
const bool must_be_find_io_handler = (found != in_impl_->io_handler_table_.end());
assert(must_be_find_io_handler);
if (must_be_find_io_handler == false) {
return 0;
}
IoHandler* io_handler = found->second;
return io_handler->Connect(network_object, ip_string, port);
}
BOOL IOCPServer::IsListening(DWORD io_handler_key)
{
IOHANDLER_MAP::const_iterator found = in_impl_->io_handler_table_.find(io_handler_key);
const bool must_be_find_io_handler = (found != in_impl_->io_handler_table_.end());
assert(must_be_find_io_handler);
if (must_be_find_io_handler == false) {
return false;
}
IoHandler* io_handler = found->second;
return io_handler->IsListening();
}
void IOCPServer::SuspendListenThread(DWORD io_handler_key)
{
IOHANDLER_MAP::const_iterator found = in_impl_->io_handler_table_.find(io_handler_key);
const bool must_be_find_io_handler = (found != in_impl_->io_handler_table_.end());
assert(must_be_find_io_handler);
if (must_be_find_io_handler == false) {
return;
}
IoHandler* io_handler = found->second;
io_handler->SuspendListenThread();
}
void IOCPServer::ResumeListenThread(DWORD io_handler_key)
{
IOHANDLER_MAP::const_iterator found = in_impl_->io_handler_table_.find(io_handler_key);
const bool must_be_find_io_handler = (found != in_impl_->io_handler_table_.end());
assert(must_be_find_io_handler);
if (must_be_find_io_handler == false) {
return;
}
IoHandler* io_handler = found->second;
io_handler->ResumeListenThread();
}
DWORD IOCPServer::GetNumberOfConnections(DWORD io_handler_key)
{
IOHANDLER_MAP::const_iterator found = in_impl_->io_handler_table_.find(io_handler_key);
const bool must_be_find_io_handler = (found != in_impl_->io_handler_table_.end());
assert(must_be_find_io_handler);
if (must_be_find_io_handler == false) {
return 0;
}
IoHandler* io_handler = found->second;
return io_handler->GetNumberOfConnections();
}
//==================================================================================================
/**
@remarks
윈속을 초기화한다.
*/
//==================================================================================================
BOOL IOCPServer::InitWinsock()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (wsaData.wVersion != MAKEWORD(2, 2))
{
WSACleanup();
return false;
}
return true;
}