I'm FanJae.

[Two Faced Poker] Day 5. Server 리팩토링 II. 서버 리팩토링에 따른 Client 대공사 본문

Toy Project/Two Faced Poker

[Two Faced Poker] Day 5. Server 리팩토링 II. 서버 리팩토링에 따른 Client 대공사

FanJae 2024. 10. 20. 00:16

1. 연관 포스트

https://fanjae.tistory.com/129 

 

[Two Faced Poker] 5. Server 리팩토링 I. 대공사

1. Server 리팩토링을 결심한 이유① Client_Event_Handler와 Game_Manager간의 독립적인 클래스라고 하기에 다소 모호한 설계 구조 개선- 기존 내가 만들었던 Client_Event_Handler는 거의 대부분의 Event를 처리하

fanjae.tistory.com

- 이 포스트에서 서버에 대한 대공사를 진행했다. 클라이언트 쪽도 이에 맞게 적당히 수정을 진행하였다.


2. 클라이언트 수정 사항

 

① PacketHandler 추가

public static string ReceivePakcet(Socket clientSocket)
{
    string message = string.Empty;
    byte[] length_buffer = new byte[4];

    int byte_length = clientSocket.Receive(length_buffer);

    if (byte_length == 0)
    {
        
    }
    if (byte_length < 0)
    {
         throw new Exception("Length_ Error");
    }
    int packet_length = BitConverter.ToInt32(length_buffer, 0);
    packet_length = IPAddress.NetworkToHostOrder(packet_length);
    if (packet_length <= 0 || packet_length > 1024)
    {
         throw new Exception("Packet_length Error");
    }

    byte[] buffer = new byte[packet_length];
    byte_length = clientSocket.Receive(buffer, packet_length, SocketFlags.None);

    if (byte_length < 0)
    {
        throw new Exception("Packet_Load_Error");
    }
    message = Encoding.UTF8.GetString(buffer, 0, byte_length);

    LogPacketToFile("Receive : " + byte_length.ToString() + " " + message);

    return message;
}
public static void SendPacket(Socket clientSocket, string message)
{

    byte[] message_bytes = System.Text.Encoding.UTF8.GetBytes(message);
    int packet_length = message_bytes.Length;

    LogPacketToFile("Send : " + message + " " + packet_length.ToString());

    byte[] length_buffer = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(packet_length));



    int byte_send_length = clientSocket.Send(length_buffer);
    byte_send_length = clientSocket.Send(message_bytes);
}

private static void LogPacketToFile(string message)
{
    string logFilePath = "packet_log.txt";  // 로그 파일 경로
    using (StreamWriter writer = new StreamWriter(logFilePath, true))
    {
        writer.WriteLine($"{DateTime.Now}: {message}");
    }
}

- 기본적인 구성은 Server와 유사하다.

- Client에서도 길이 정보를 먼저 받은 다음에 실제 메시지를 받는다. (전송도 이는 마찬가지다.)

- 실제로 어떤 정보 전달 하는지 확인하기 위해서 텍스트 파일로 떨궈놨다.

 

private void RoomHandle(string message)
{
    if ((message.Length >= Constants.ROOM_EVENT.Length + Constants.UPDATE_ID.Length && (message.Substring(Constants.ROOM_EVENT.Length, Constants.UPDATE_ID.Length) == Constants.UPDATE_ID)))
    {
        if (InvokeRequired)
        {
            Invoke(new Action(() =>
            {
                Vs_ID_Label.Text = "ID : " + message.Substring(Constants.ROOM_EVENT.Length + Constants.UPDATE_ID.Length);
            }));
        }
    }
    else if ((message.Length >= Constants.ROOM_EVENT.Length + Constants.UPDATE_READY_STATE.Length && (message.Substring(Constants.ROOM_EVENT.Length, Constants.UPDATE_READY_STATE.Length) == Constants.UPDATE_READY_STATE)))
    {
        string State = message.Substring(Constants.ROOM_EVENT.Length + Constants.UPDATE_READY_STATE.Length);
        if (State == Constants.READY)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() =>
                {
                    Vs_ID_Label.Text = "<준비>";
                }));
            }
        }
        else if (State == Constants.DONE)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() =>
                {
                    Vs_ID_Label.Text = "<완료>";
                }));
            }
        }
    }
}
private void EventHandle(string message)
{
    try
    {
        if (message.Substring(Constants.GAME_CLIENT_EVENT.Length) == Constants.READY_DONE)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() =>
                {
                    Vs_Ready.Text = "<완료>";
                }));
            }
        }
        else if (message.Substring(Constants.GAME_CLIENT_EVENT.Length) == Constants.READY_NOT_DONE)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() =>
                {
                    Vs_Ready.Text = "<준비>";
                }));
            }
        }
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message + " " + message);
    }
}

- 추후에는 Server 처리하듯이 개선(substring을 잘라서 보내는 방식) 계획이다. 

- Invoke 사용시 Invoke Method 호출 여부가 필요한 지 매번 확인하도록 처리해놨다.

- Invoke가 필요한 경우에만 delegate method를 생성하도록 만들어놨다.

- Invoke 처리 유무에 따라서 UI 갱신의 안정성이 확실하게 갈리는거 같다.


3. 리팩토링 후기 

- 서버 구조 개선을 하면서 확실히 서버와 클라이언트 간 주고 받는 데이터의 길이와 메시지가 확실하게 보장된다.

- 또한, Room_Manager, Game_Manager와 같이 나눠 처리하여 확실하게 각자의 역할 구분이 가능해졌다.

- 이 내용을 토대로 게임 로직을 다뤄보고자 한다.

 

 

 

Comments