지금까지는 RecvBuffer에 그냥 다 때려박았는데 생각해보면 처리되지 않은 데이터들을 그냥 다 버퍼에 넣어버리는 건 당연히 하면 안될 일이다.
물론 아직 완성되지 않았기 때문에 그렇지만 이런 것들을 생각하면서 코드를 작성해보도록 하자.
이번엔 저번 게시글에 이어 Send하는 부분을 만들어 볼 것이다.
지금 작성하는 코드는 Send의 단계를 이해하기 위한 코드이므로 이후 삭제될 코드들이 꽤 많이 들어간다.
Session.h
#pragma once
#include "IocpCore.h"
#include "IocpEvent.h"
#include "NetAddress.h"
class Service;
//------------
// Session
//------------
class Session : public IocpObject
{
friend class Listener;
friend class IocpCore;
friend class Service;
public:
Session();
virtual ~Session();
public:
/* 외부에서 사용 */
void Send(BYTE* buffer, int32 len);
void Disconnect(const WCHAR* cause);
shared_ptr<Service> GetService() { return _service.lock(); }
void SetService(shared_ptr<Service> service) { _service = service; }
public:
/* 정보 관련 */
void SetNetAddress(NetAddress address) { _netAddress = address; }
NetAddress GetNetAddress() { return _netAddress; }
SOCKET GetSocket() { return _socket; }
bool IsConnected() { return _connected; }
SessionRef GetSessionRef() { return static_pointer_cast<Session>(shared_from_this()); }
private:
/* 인터페이스 구현 */
virtual HANDLE GetHandle() override;
virtual void Dispatch(class IocpEvent* iocpEvent, int32 numOfByte = 0) override;
private:
/* 전송 관련 */
void RegisterConnect();
void RegisterRecv();
void RegisterSend(SendEvent* sendEvent);
void ProcessConnect();
void ProcessRecv(int32 numOfBytes);
void ProcessSend(SendEvent* sendEvent, int32 numOfBytes);
void HandleError(int32 errorCode);
protected:
/* 컨텐츠 코드에서 오버로딩 */
virtual void OnConnected() {}
virtual int32 OnRecv(BYTE* buffer, int32 len) { return len; }
virtual void OnSend(int32 len) {}
virtual void OnDisconnected() {}
public:
// send, recv 관련 버퍼 들어가야됨
// TEMP
BYTE _recvBuffer[1000];
private:
weak_ptr<Service> _service;
SOCKET _socket = INVALID_SOCKET;
NetAddress _netAddress = {};
Atomic<bool> _connected = false;
private:
USE_LOCK;
/* 수신 관련 */
/* 송신 관련 */
private:
/* IocpEvent 재사용 */
RecvEvent _recvEvent;
};
Session.cpp
#include "pch.h"
#include "Session.h"
#include "SocketUtils.h"
#include "Service.h"
//------------
// Session
//------------
Session::Session()
{
_socket = SocketUtils::CreateSocket();
}
Session::~Session()
{
SocketUtils::Close(_socket);
}
void Session::Send(BYTE* buffer, int32 len)
{
// 생각할 문제
// 1) 버퍼 관리
// 2) sendEvent 한 개? 여러개? WSASend 중첩?
// TEMP (일단 실시간으로 만드는 형태)
SendEvent* sendEvent = Xnew<SendEvent>();
sendEvent->owner = shared_from_this(); // ADD_REF
sendEvent->buffer.resize(len);
::memcpy(sendEvent->buffer.data(), buffer, len);
WRITE_LOCK;
// Send를 할 때마다 하는 것보다는 모았다 한 번에 보내도록 유도하는 것이 성능상 유리함
RegisterSend(sendEvent);
}
void Session::Disconnect(const WCHAR* cause)
{
if (_connected.exchange(false) == false)
return;
// TEMP
wcout << "Disconnect : " << cause << endl;
OnDisconnected(); // 컨텐츠 코드에서 오버로딩
SocketUtils::Close(_socket);
GetService()->ReleaseSession(GetSessionRef());
}
HANDLE Session::GetHandle()
{
return reinterpret_cast<HANDLE>(_socket);
}
// iocpEvent가 recv나 send같은 이벤트를 만들면 Dispatch가 처리
void Session::Dispatch(IocpEvent* iocpEvent, int32 numOfByte)
{
switch (iocpEvent->eventType)
{
case EventType::Connect:
ProcessConnect();
break;
case EventType::Recv:
ProcessRecv(numOfByte);
break;
case EventType::Send:
ProcessSend(static_cast<SendEvent*>(iocpEvent), numOfByte);
break;
default:
break;
}
}
void Session::RegisterConnect()
{
}
void Session::RegisterRecv()
{
if (IsConnected() == false)
return;
_recvEvent.Init();
_recvEvent.owner = shared_from_this(); // ADD_REF
WSABUF wsaBuf;
wsaBuf.buf = reinterpret_cast<char*>(_recvBuffer);
wsaBuf.len = len32(_recvBuffer);
DWORD numOfBytes = 0;
DWORD flags = 0;
if (SOCKET_ERROR == ::WSARecv(_socket, &wsaBuf, 1, OUT &numOfBytes, OUT &flags, &_recvEvent, nullptr))
{
int32 errorCode = ::WSAGetLastError();
if (errorCode != WSA_IO_PENDING)
{
HandleError(errorCode);
_recvEvent.owner = nullptr; // RELEASE_REF
}
}
}
void Session::RegisterSend(SendEvent* sendEvent)
{
if (IsConnected() == false)
return;
WSABUF wsaBuf;
wsaBuf.buf = (char*)sendEvent->buffer.data();
wsaBuf.len = (ULONG)sendEvent->buffer.size();
DWORD numOfByte = 0;
if (SOCKET_ERROR == ::WSASend(_socket, &wsaBuf, 1, OUT &numOfByte, 0, sendEvent, nullptr))
{
int32 errorCode = ::WSAGetLastError();
if (errorCode != WSA_IO_PENDING)
{
HandleError(errorCode);
sendEvent->owner = nullptr; // RELEASE_REF
Xdelete(sendEvent);
}
}
}
void Session::ProcessConnect()
{
_connected.store(true);
// 세션 등록
GetService()->AddSession(GetSessionRef());
// 컨텐츠 코드에서 오버로딩
OnConnected();
// 수신 등록
RegisterRecv();
}
void Session::ProcessRecv(int32 numOfBytes)
{
_recvEvent.owner = nullptr; // RELEASE_REF
if (numOfBytes == 0)
{
Disconnect(L"Recv 0");
return;
}
// 컨텐츠 코드에서 오버로딩
OnRecv(_recvBuffer, numOfBytes);
// 수신 등록
RegisterRecv();
}
void Session::ProcessSend(SendEvent* sendEvent, int32 numOfBytes)
{
sendEvent->owner = nullptr; // RELEASE_REF
Xdelete(sendEvent);
if (numOfBytes == 0)
{
Disconnect(L"Send 0");
}
// 컨텐츠 코드에서 오버로딩
OnSend(numOfBytes);
}
void Session::HandleError(int32 errorCode)
{
switch (errorCode)
{
case WSAECONNRESET:
case WSAECONNABORTED:
Disconnect(L"HandleError");
break;
default:
// TODO : Log
cout << "Handle Error : " << errorCode << endl;
break;
}
}
Send 부분만 만들어 보았다.
Main.cpp
#include "pch.h"
#include "CorePch.h"
#include <Windows.h>
#include "ThreadManager.h"
#include "Service.h"
#include "Session.h"
class GameSession : public Session
{
public:
virtual int32 OnRecv(BYTE* buffer, int32 len) override
{
// Echo
cout << "OnRecv Len = " << len << endl;
Send(buffer, len);
return len;
}
virtual void OnSend(int32 len) override
{
cout << "OnSend Len = " << len << endl;
}
};
int main()
{
ServerServiceRef service = MakeShared<ServerService>(
NetAddress(L"127.0.0.1", 7777),
MakeShared<IocpCore>(),
MakeShared<GameSession>, // TODO : SessionManager 등
100);
ASSERT_CRASH(service->Start());
for (int32 i = 0; i < 5; i++)
{
GThreadManager->Launch([=]()
{
while (true)
{
service->GetIocpCore()->Dispatch();
}
});
}
GThreadManager->Join();
}
테스트를 위해 세션을 상속받아 오버라이드를 해주었다.
실행화면
클라이언트의 연결이 끊겼을 때에도 문제없이 작동하는 모습이다.
'서버 > 네트워크 라이브러리' 카테고리의 다른 글
4. Session#1 (0) | 2025.01.01 |
---|---|
3. Service (0) | 2024.11.29 |
2. IocpCore (0) | 2024.10.05 |
1. Socket Utils (0) | 2024.10.01 |