이번 포스트에서는 지난 포스트에 이어 클라이언트 단계에서 채팅 프로그램을 만들겠습니다.
1) Connector
public class Connector
{
.
.
.
// 연결을 수행하는 함수
public void Connect(IPEndPoint endPoint, Func<Session> sessionFactory, int connectCount = 1)
{
for (int i = 0; i < connectCount; i++)
{
// 서버 소켓 생성
Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_sessionFactory = sessionFactory;
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += OnConnectCompleted;
args.RemoteEndPoint = endPoint;
args.UserToken = socket;
RegisterConnect(args);
}
}
.
.
.
}
우선, 기존의 Connector의 Connect 함수를 수정하여
Connect를 connectCount만큼 수행하도록 변경하였습니다.
2) PacketFormat
class PacketFormat
{
// {0} 패킷 등록
public static string managerFormat =
@" using ServerCore;
using System;
using System.Collections.Generic;
class PacketManager
{{
static PacketManager _instance = new PacketManager();
public static PacketManager Instance {{ get {{ return _instance; }} }}
PacketManager()
{{
Register();
}}
.
.
.
";
.
.
.
}
또 Client와 Server 별 자동으로 생성되는 PacketManager의 자동생성양식을 위와 같이 변경하였습니다.
프로퍼티인 Instance에서 PacketManager를 생성하여 리턴하고,
PacketManager의 생성자에서 Register를 수행하도록 수정하였습니다.
이제 클라이언트 단계로 넘어가보겠습니다.
3) 클라이언트의 PacketHandler
class PacketHandler
{
// 인자는 어떤 세션에서 , 어떤 패킷이 조립되었는지를 의미함
public static void S_ChatHandler(PacketSession session, IPacket packet)
{
S_Chat chatPacket = packet as S_Chat;
ServerSession serverSession = session as ServerSession;
Console.WriteLine(chatPacket.chat);
}
}
클라이언트의 패킷의 경우, 로그를 찍는 작업을 수행합니다.
4) 클라이언트의 SessionManager
class SessionManager
{
static SessionManager _session = new SessionManager();
public static SessionManager Instance { get { return _session; } }
List<ServerSession> _sessions = new List<ServerSession>();
object _lock = new object();
public ServerSession Generate()
{
lock (_lock)
{
ServerSession session = new ServerSession();
_sessions.Add(session);
return session;
}
}
public void SendForEach()
{
lock (_lock)
{
foreach (ServerSession eachSession in _sessions)
{
C_Chat chatPacket = new C_Chat();
chatPacket.chat = $"Hello Server !";
ArraySegment<byte> segment = chatPacket.Write();
eachSession.Send(segment);
}
}
}
}
서버의 SessionManager와 비슷하게 클라이언트의 SessionManager도 동작합니다.
Generate에서는 새로운 ServerSession을 생성하고 List에 저장한 후 리턴합니다.
SendForEach에서는 챗 메세지를 다른 ServerSession에게 전송합니다.
5) ServerSession
class ServerSession : PacketSession
{
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
}
public override void OnDisconnected(EndPoint endPoint)
{
Console.WriteLine($"OnDisconnected : {endPoint}");
}
public override void OnRecvPacket(ArraySegment<byte> buffer)
{
PacketManager.Instance.OnRecvPacket(this, buffer);
}
public override void OnSend(int numOfBytes)
{
}
}
Send의 경우, 이미 클라이언트의 SessionManager의 SendForEach에서 수행하므로 생략하였습니다.
최종적으로 PacketGenerator를 빌드한 후, bat 파일을 실행하여
자동생성된 파일들로 파일 구성을 변경하였습니다.
이후 Client와 Server의 메인 코드를 변경하겠습니다.
6) Client
class Client
{
static void Main(string[] args)
{
// DNS를 사용
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
Connector connector = new Connector();
connector.Connect(endPoint, () => { return SessionManager.Instance.Generate(); }, 10);
while (true)
{
try
{
SessionManager.Instance.SendForEach();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Thread.Sleep(250);
}
}
}
Client의 Main 함수에서는 총 10개의 ServerSession을 만들어 수행하도록 하겠습니다.
7) Server의 GameRoom
class GameRoom
{
List<ClientSession> _sessions = new List<ClientSession>();
object _lock = new object();
public void Broadcast(ClientSession session, string chat)
{
S_Chat packet = new S_Chat();
packet.playerID = session.clientSessionID;
packet.chat = $"{chat} I am {packet.playerID}";
ArraySegment<byte> segment = packet.Write();
lock (_lock)
{
foreach (ClientSession eachSession in _sessions)
eachSession.Send(segment);
}
}
.
.
.
}
GameRoom에서는 ChatPacket의 Chat 프로퍼티를 위와 같이 설정하였습니다.
최종 실행결과는 다음과 같습니다.
문제없이 동작하는 것을 확인하였지만,
지금까지 작성한 과정에서 스레드의 양을 조금만 늘려도 부하가 발생하게 됩니다.
그 이유는 이어지는 포스트에서 자세히 알아보겠습니다.
'C#' 카테고리의 다른 글
JobQueue #1 (0) | 2025.01.22 |
---|---|
Command 패턴 (0) | 2025.01.22 |
채팅 테스트 #1 (0) | 2025.01.21 |
Packet Generator #6 (0) | 2025.01.20 |
Packet Generator #5 (0) | 2025.01.20 |