PacketProcessor 클래스
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using CSBaseLib;
namespace ChatServer
{
class PacketProcessor
{
// 스레드 실행 여부 체크용 변수
bool IsThreadRunning = false;
// 처리 스레드 객체 선언
System.Threading.Thread ProcessThread = null;
//receive쪽에서 처리하지 않아도 Post에서 블럭킹 되지 않는다.
//BufferBlock<T>(DataflowBlockOptions) 에서 DataflowBlockOptions의 BoundedCapacity로 버퍼 가능 수 지정. BoundedCapacity 보다 크게 쌓이면 블럭킹 된다
BufferBlock<ServerPacketData> MsgBuffer = new BufferBlock<ServerPacketData>();
// 유저 매니저 선언
UserManager UserMgr = new UserManager();
// 방 번호 범위를 나타내는 튜플
Tuple<int,int> RoomNumberRange = new Tuple<int, int>(-1, -1);
List<Room> RoomList = new List<Room>();
Dictionary<int, Action<ServerPacketData>> PacketHandlerMap = new Dictionary<int, Action<ServerPacketData>>();
// 일반적인 패킷 처리 담당
PKHCommon CommonPacketHandler = new PKHCommon();
// 방 관련 패킷 처리 담당
PKHRoom RoomPacketHandler = new PKHRoom();
- BufferBlock : 비동기적으로 데이터를 처리할 때 사용 / 데이터를 임시로 저장하는 컨테이너 같은 거
- System.Threading.Tasks.Dataflow 에서 제공
- 메시지를 받으면 일단 여기에 넣어놓고, 필요할 때 처리
- 처리할 때 다른 메시지를 기다리지 않아도 된다
- 처리할 메시지가 있으면 바로 처리하고, 없으면 기다리지 않고 다음 일 수행 가능
- ServerPacketData : 패킷 형식
- 패킷 사이즈 / 세션ID / 패킷ID / 타입 / 바디데이터
CreateAndStart
//TODO MainServer를 인자로 주지말고, func을 인자로 넘겨주는 것이 좋다
public void CreateAndStart(List<Room> roomList, MainServer mainServer)
{
// 최대 유저 수 = 최대 방 수 * 방 당 최대 유저 수
var maxUserCount = MainServer.ServerOption.RoomMaxCount * MainServer.ServerOption.RoomMaxUserCount;
// 최대 유저 수 초기화
UserMgr.Init(maxUserCount);
// 클래스 변수 RoomList에 매개변수로 받은 리스트 복사
RoomList = roomList;
// 첫 번째 방 번호
var minRoomNum = RoomList[0].Number;
// 마지막 방 번호
var maxRoomNum = RoomList[0].Number + RoomList.Count() - 1;
// 현재 방 번호 범위(첫번째 방 ~ 마지막 방) 튜플 생성
RoomNumberRange = new Tuple<int, int>(minRoomNum, maxRoomNum);
// 각 패킷 핸들러(일반, 방 전용)의 패킷아이디/함수포인터 딕셔너리 추가하는 함수 호출
RegistPacketHandler(mainServer);
// 스레드가 돌고있다고 변수 수정
IsThreadRunning = true;
// 스레드 할당 후 Process 메서드를 실행하도록 설정
ProcessThread = new System.Threading.Thread(this.Process);
// 스레드 시작
ProcessThread.Start();
}
- 유저 매니저 = 방에 대한 유저의 매니저가 아니고 진짜 접속한 유저들에 대한 매니저?
Destroy
public void Destory()
{
IsThreadRunning = false;
MsgBuffer.Complete();
}
- Destroy 시 아까만든 임시 컨테이너? BufferBlock 형태의 MsgBuffer 완료 상태로 표시
- 더 이상 새로운 데이터 받지 않고 모든 데이터가 처리되었음을 표시
InsertPacket
public void InsertPacket(ServerPacketData data)
{
MsgBuffer.Post(data);
}
- BufferBlock 형태의 MsgBuffer에 받은 패킷 추가
RegistPacketHandler
void RegistPacketHandler(MainServer serverNetwork)
{
CommonPacketHandler.Init(serverNetwork, UserMgr);
CommonPacketHandler.RegistPacketHandler(PacketHandlerMap);
RoomPacketHandler.Init(serverNetwork, UserMgr);
RoomPacketHandler.SetRooomList(RoomList);
RoomPacketHandler.RegistPacketHandler(PacketHandlerMap);
}
- 일반, 방 전용 패킷 핸들러 세팅
- 패킷 ID 에 해당하는 함수들을 딕셔너리에 추가
Process
void Process()
{
// 스레드가 실행중인 동안 계속 돔
while (IsThreadRunning)
{
//System.Threading.Thread.Sleep(64); //테스트 용
try
{
// MsgBuffer에서 패킷 받기
var packet = MsgBuffer.Receive();
// 패킷의 아이디에 해당하는 값 확인
if (PacketHandlerMap.ContainsKey(packet.PacketID))
{
// 패킷 아이디에 맞는 함수 호출
PacketHandlerMap[packet.PacketID](packet);
}
else
{
System.Diagnostics.Debug.WriteLine("세션 번호 {0}, PacketID {1}, 받은 데이터 크기: {2}", packet.SessionID, packet.PacketID, packet.BodyData.Length);
}
}
catch (Exception ex)
{
IsThreadRunning.IfTrue(() => MainServer.MainLogger.Error(ex.ToString()));
}
}
- Receive : 버퍼 블록에서 데이터 받기
- 데이터가 들어올때까지 대기하고, 들어오면 해당 데이터 반환