참고 할 수 있도록 샘플 프로그램을 만들어봤습니다.
자잘한 예외 처리는 하지 않았으며, 채팅 기능만 집중하고 나머지 기능은 동작성만 확인할 수 있도록 간소화 하였습니다.,
| 최초 화면

| test 연결
![image]()
| 디모이 연결

| 서버 화면

| 대화

패킷을 주고 받는 기능을 ChatPacket.cs
에서 처리하였습니다.
| 패킷 받기
public static async Task<IChatPacket> ReceiveAsync(Stream s)
{
try
{
var temp = new byte[1];
await s.ReadExactlyAsync(temp, 0, 1);
var command = (CommandType)temp[0];
IChatPacket result = command switch
{
CommandType.REQ_HELLO => ChatHelloRequest.Receive(s),
CommandType.RES_HELLO => ChatHelloResponse.Receive(s),
CommandType.REQ_GOODBYE => ChatGoodbyeRequest.Receive(s),
CommandType.RES_GOODBYE => ChatGoodbyeResponse.Receive(s),
CommandType.REQ_MESSAGE => ChatMessageRequest.Receive(s),
CommandType.RES_MESSAGE => ChatMessageResponse.Receive(s),
CommandType.EVT_MESSAGE => ChatMessageEvent.Receive(s),
CommandType.EVT_INFO => ChatInfoEvent.Receive(s),
_ => new ChatPacketError(ChatPacketErrorKind.InvalidCommand)
};
return result;
}
// 강제 연결 종료
catch (EndOfStreamException)
{
return new ChatPacketError(ChatPacketErrorKind.ForceDisconnected);
}
// 기타 알수 없는 이유 (패킷 비정상)
catch (Exception)
{
return new ChatPacketError(ChatPacketErrorKind.InvalidPacket);
}
}
| 패킷 보내기
public static void Send(IChatPacket packet, NetworkStream s)
{
s.WriteByte((byte)packet.Command);
packet.Send(s);
s.Flush();
}
주거니 받거니 하는 패킷의 종류는 1 바이트의 Command로 구분하였으며 각각의 패킷은 IChatPacket
인터페이스를 구현합니다.
public interface IChatPacket
{
CommandType Command { get; }
void Send(Stream s);
}
public interface IChatPacket<T> : IChatPacket
{
abstract static T Receive(Stream s);
}
다음은 메시지를 보내는 패킷의 코드입니다. (패킷 구성은 BinaryReader, BinaryWriter
를 이용)
| ChatMessageRequest
public record ChatMessageRequest(string Authtoken, string Message) : IChatPacket<ChatMessageRequest>
{
public CommandType Command => CommandType.REQ_MESSAGE;
public static ChatMessageRequest Receive(Stream s)
{
using var br = new BinaryReader(s, Encoding.UTF8, true);
return new ChatMessageRequest(br.ReadString(), br.ReadString());
}
public void Send(Stream s)
{
using var bw = new BinaryWriter(s, Encoding.UTF8, true);
bw.Write(Authtoken);
bw.Write(Message);
}
}
패킷은 비동기 메소드인 ReceivedAsync()
에 의해 수신되며 최초 연결시 등록됩니다.
_ = _client.ReceiveAsync();
_client.ReceivedChatPacketEvent += _client_ReceivedChatPacketEvent;
이후 이벤트를 통해 수신된 패킷을 처리합니다.
private void _client_ReceivedChatPacketEvent(object? sender, ReceivedChatPacketEventArgs e)
{
var packet = e.Packet;
if (packet is ChatPacketError error)
{
// 에러 처리 (생략함)
return;
}
if (packet is ChatInfoEvent info)
{
if (info.InfoKind is ChatInfoKind.Users)
{
Users = info.InfoMap.Select(x => $"{x.Value}({x.Key})").ToList();
}
}
else if (packet is ChatHelloResponse response)
{
// 인증키 처리 (생략함)
}
else if (packet is ChatMessageEvent message)
{
if (_lastDate != DateOnly.FromDateTime(DateTime.Now))
{
_lastDate = DateOnly.FromDateTime(DateTime.Now);
Messages += $"[{_lastDate}]\r\n";
}
Messages += $"[{TimeOnly.FromDateTime(DateTime.Now)}, {message.Nickname}] {message.Message}\r\n";
}
_ = e.Client.ReceiveAsync();
}
참고가 되었으면 하네요.