게임서버 강의/네트워크 라이브러리 10

10. Packet Session

TCP의 특성상 내가 보낸 패킷이 한 번에 도착한다는 보장이 없다. 그래서 전체 패킷이 도착했는지 확인하기 위한 수단이 필요하다.그리고 이를 위해서는 프로토콜을 정의해야 하는데 어떤 식으로 데이터의 끝이라는 것을 알 수 있을까? 단순히 생각하면 끝에 특수문자를 보내서 그 문자가 나오면 끝이다라고 하면 될 거 같다. 0x0001이라는 데이터를 패킷의 끝이라고 정의했다고 가정해보자. 운 나쁘게 데이터를 보내면서 0x0001이라는 데이터가 보내지게 되면 결국 이상하게 동작하게 된다. 그래서 당연하게도 이 방법은 사용할 수 없다. mmo에서 가장 정석이라고 볼 수 있는 방법은 패킷헤더를 사용하는 것이다. 패킷을 보낼 때 헤더에 정보를 담아 추가해서 보내는 것이다. Session.h//--------------..

9. SendBuffer Pooling

현재 버퍼를 만들 때 여유공간을 넉넉히 두고 생성을 한다.  len을 파라미터로 받는다는 것은 보낼 데이터의 크기를 알고 있다는 것인데 그럼 len 크기 만큼 버퍼를 만들면 되지 않을까? 지금 코드를 보면 그렇지만 후에는 크기가 얼마인지 모르는 경우가 생기기 때문에 이를 위해서 미리 넉넉하게 버퍼를 만들어 두는 것이다. 하지만 고작 1 바이트 크기의 데이터를 보내는데 4096 바이트를 할당하는 것은 비효율적으로 보인다. 따라서 이번에 할 것은 SendBufferChunk라는 것을 만들어 처음에 큰 공간을 미리 할당받은 다음에 내가 필요한 만큼만 잘라서 사용하는 식으로 만들 것이다. SendBuffer.h#pragma onceclass SendBufferChunk;//---------------// Sen..

8. SendBuffer

현재 Send의 코드를 보자 SendEvent* sendEvent = Xnew(); sendEvent->owner = shared_from_this(); // ADD_REF sendEvent->buffer.resize(len); ::memcpy(sendEvent->buffer.data(), buffer, len);이 부분을 보면 memcpy를 하게 되는데 이때 복사 비용이 들어간다는 문제점이 있다. 후에 가면 send를 할 일이 굉장히 많이 생길 것이다. 예를 들어 몬스터가 생성되었을 때 주변의 모든 플레이어들에게 해당 정보를 전송 해줘야 하는데 그럴 때마다 복사를 해서 보낸다는 것은 듣자마자 비효율적이라는 것을 느낄 수 있다. 가장 먼저 직관적이고 간단한 방법으로 SendBuffer를 만들어 볼 것이다..

7. RecvBuffer

Recv같은 경우는 멀티스레드 환경을 고려하지 않아도 된다. 한 번에 한 스레드만 ResisterRecv와 ProcessRecv로 들어올 것이기 때문이다. 낚시대가 1개라고 생각하면 된다. 그럼에도 현재의 코드와 같이 RecvBuffer를 그냥 사용하는 것은 문제가 있다. 지금 TCP서버를 만들고 있는데 TCP의 경우는 패킷과의 바운더리가 없다. 이는 상대방이 100바이트를 보내더라도 그것이 잘려서 20바이트만 넘어올 수 있다는 얘기이다. 그럼 뭐가 문제냐? 대부분의 경우 패킷의 완전체를 받아야 처리를 할 수 있을 것이다. 그렇기 때문에 지금처럼 패킷을 덮어쓰면 안되는 것이다. 그럼 이제 Recv 패킷을 관리하는 클래스를 만들어보자. 이 클래스에서 끊어져서 온 패킷을 합치기도 하도 이것저것 작업을 할 것..

6. Session#3

현재는 세션을 생성할 때 소켓도 함께 생성하고 연결이 끊기면 소켓을 닫아준다. 하지만 소켓을 만드는 작업에 꽤 많은 비용이 들어가기 때문에 소켓을 재사용하는 코드로 바꿔줄 것이다.  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: /* 외부에서 ..

5. Session#2

지금까지는 RecvBuffer에 그냥 다 때려박았는데 생각해보면 처리되지 않은 데이터들을 그냥 다 버퍼에 넣어버리는 건 당연히 하면 안될 일이다.물론 아직 완성되지 않았기 때문에 그렇지만 이런 것들을 생각하면서 코드를 작성해보도록 하자.이번엔 저번 게시글에 이어 Send하는 부분을 만들어 볼 것이다. 지금 작성하는 코드는 Send의 단계를 이해하기 위한 코드이므로 이후 삭제될 코드들이 꽤 많이 들어간다. Session.h#pragma once#include "IocpCore.h"#include "IocpEvent.h"#include "NetAddress.h"class Service;//------------// Session//------------class Session : public IocpObj..

3. Service

지난번에 iocpCore를 만들어서 실행해보았고 이를 좀 더 편하게 관리하기 위해 Service라는 것을 도입했다.이에 맞춰 기존의 라이브러리를 조금 수정했다. Service.h#pragma once#include "NetAddress.h"#include "IocpCore.h"#include "Listener.h"#include enum class ServiceType : uint8{ Server, Client};//-------------// Service//-------------using SessionFactory = function;class Service : public enable_shared_from_this{public: Service(ServiceType type, NetAddress ..

2. IocpCore

본격적으로 IOCP를 구현해보자 IocpCore.h#pragma once// Iocp에 등록할 수 있는 객체//---------------// IocpObject//---------------class IocpObject{public: virtual HANDLE GetHandle() abstract; virtual void Dispatch(class IocpEvent* iocpEvent, int32 numOfByte = 0) abstract;};//-------------// IocpCore//-------------class IocpCore{public: IocpCore(); ~IocpCore(); HANDLE GetHandle() { return _iocpHandle; }; // 세션, 소켓 생..

1. Socket Utils

본격적으로 라이브러리를 쌓아올리는 과정이다. 먼저 소켓을 초기화하고 인자를 설정하는 등 소켓에 대한 내용이다. SocketUtils.h#pragma once#include "NetAddress.h"// 매핑을 해줘서 편하게 사용하기 위함//----------------// Socket Utils//----------------class SocketUtils{public: static LPFN_CONNECTEX ConnectEx; static LPFN_DISCONNECTEX DisconnectEx; static LPFN_ACCEPTEX AcceptEx;public: static void Init(); static void Clear(); static bool BindWindowsFunction(SOC..