본문 바로가기

프로그램/유니티 네트워크

[유니티 네트워크] TcpServer, TcpClient 구조체 전송 (Socket)

728x90
반응형

 ※ 아래 예제는 TcpServer, TcpClient 기본 형태로 유니티에서 바로 사용하기에는 문제가 약간 있습니다.

 Client가 접속하기 전까지 Server가 기다리며, Client가 끊기었다가 재접속하면 연결이 되지 않습니다.

 하지만, 위 2가지를 무시하고 사용한다면, 아래 예제를 사용해도 됩니다.

 

1. 내용

 (1) 권장 포트 번호 : 49152 ~ 65535

 

포트 번호

내용

0

사용하지 않음.

1 ~1023

잘 알려진 포트(Well-known port)

1024 ~ 49151

등록된 포트(Registered port)

49152 ~ 65535

동적 포트(Dynamic port)

 

 (2) 전송 패키지 사이즈

  Tcp 전송 패키지 크기는 Ethernet(V2)에서 MTU(Maximum Transmission Unit)는 1500 Byte, MSS(Maximum Segment Size)는 Tcp헤더와 IP헤더를 제외하면 1460 Byte 입니다.

  하지만, 편의상 1024 byte 이내가 사용하기 적당합니다.

 

 (3) 소스 내용

  - 유니티와 유니티, 또는 C#과의 통신, 또는 C++와의 통신

  - 클래스 : Socket

  - 개체 직렬화 : Marshal

  - 전송 패킷 : 구조체 ↔ 바이트 배열

 

 

2. 소스

2.1 Server.cs

using System;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

public class Server : MonoBehaviour
{
    private Socket m_Server, m_Client;
    public int m_Port = 50001;
    public ToClientPacket m_SendPacket = new ToClientPacket();
    public ToServerPacket m_ReceivePacket = new ToServerPacket();
    private EndPoint m_RemoteEndPoint;

    void Start()
    {
        InitServer();
    }

    void Update()
    {
        Receive();
        Send();
    }

    void OnApplicationQuit()
    {
        CloseServer();
    }

    void InitServer()
    {
        // SendPacket에 배열이 있으면 선언 해 주어야 함.
        m_SendPacket.m_IntArray = new int[2];

        m_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        m_RemoteEndPoint = new IPEndPoint(IPAddress.Any, m_Port);
        m_Server.Bind(m_RemoteEndPoint);
        m_Server.Listen(10); // client 접속을 기다림.
        m_Client = m_Server.Accept(); // client가 하나만 사용.
    }

    void Receive()
    {
        int receive = 0;
        byte[] packet = new byte[1024];

        try
        {
            receive = m_Client.Receive(packet);
        }

        catch (Exception ex)
        {
            // Debug.Log(ex.ToString());
            return;
        }

        m_ReceivePacket = ByteArrayToStruct<ToServerPacket>(packet);

        if (receive > 0)
        {
            DoReceivePacket(); // 받은 값 처리
        }
    }

    void DoReceivePacket()
    {
        Debug.LogFormat($"m_IntArray[0] = {m_ReceivePacket.m_IntArray[0]} " +
            $"m_IntArray[1] = {m_ReceivePacket.m_IntArray[1] } " +
            $"FloatlVariable = {m_ReceivePacket.m_FloatlVariable} " +
            $"StringlVariable = {m_ReceivePacket.m_StringlVariable}" +
            $"BoolVariable = {m_ReceivePacket.m_BoolVariable} " +
            $"IntlVariable = {m_ReceivePacket.m_IntVariable} ");
        //출력: m_IntArray[0] = 7 m_IntArray[1] = 47 FloatlVariable = 2020 StringlVariable = Coder ZeroBoolVariable = True IntlVariable = 13 
    }

    void Send()
    {
        try
        {
            SetSendPacket();

            byte[] sendPacket = StructToByteArray(m_SendPacket);
            m_Client.Send(sendPacket, 0, sendPacket.Length, SocketFlags.None);
        }

        catch (Exception ex)
        {
            //Debug.Log(ex.ToString());
            return;
        }
    }

    void SetSendPacket()
    {
        m_SendPacket.m_BoolVariable = true;
        m_SendPacket.m_IntVariable = 13;
        m_SendPacket.m_IntArray[0] = 7;
        m_SendPacket.m_IntArray[1] = 47;
        m_SendPacket.m_FloatlVariable = 2020;
        m_SendPacket.m_StringlVariable = "Coder Zero";
    }

    void CloseServer()
    {
        if (m_Client != null)
        {
            m_Client.Close();
            m_Client = null;
        }

        if (m_Server != null)
        {
            m_Server.Close();
            m_Server = null;
        }
    }

    byte[] StructToByteArray(object obj)
    {
        int size = Marshal.SizeOf(obj);
        byte[] arr = new byte[size];
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(obj, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        Marshal.FreeHGlobal(ptr);
        return arr;
    }

    T ByteArrayToStruct<T>(byte[] buffer) where T : struct
    {
        int size = Marshal.SizeOf(typeof(T));
        if (size > buffer.Length)
        {
            throw new Exception();
        }

        IntPtr ptr = Marshal.AllocHGlobal(size);
        Marshal.Copy(buffer, 0, ptr, size);
        T obj = (T)Marshal.PtrToStructure(ptr, typeof(T));
        Marshal.FreeHGlobal(ptr);
        return obj;
    }
}

 

2.2 Client.cs 

using System;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

public class Client : MonoBehaviour
{
    private Socket m_Client;
    public string m_Ip = "127.0.0.1";
    public int m_Port = 50001;
    public ToServerPacket m_SendPacket = new ToServerPacket();
    public ToClientPacket m_ReceivePacket = new ToClientPacket();
    private IPEndPoint m_ServerIpEndPoint;
    private EndPoint m_RemoteEndPoint;

    void Start()
    {
        InitClient();
    }


    void Update()
    {
        Receive();
        Send();
    }

    void OnApplicationQuit()
    {
        CloseClient();
    }

    void InitClient()
    {
        // SendPacket에 배열이 있으면 선언 해 주어야 함.
        m_SendPacket.m_IntArray = new int[2];

        m_ServerIpEndPoint = new IPEndPoint(IPAddress.Parse(m_Ip), m_Port);
        m_Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        m_Client.Connect(m_ServerIpEndPoint);
    }

    void SetSendPacket()
    {
        m_SendPacket.m_BoolVariable = true;
        m_SendPacket.m_IntVariable = 13;
        m_SendPacket.m_IntArray[0] = 7;
        m_SendPacket.m_IntArray[1] = 47;
        m_SendPacket.m_FloatlVariable = 2020;
        m_SendPacket.m_StringlVariable = "Coder Zero";
    }

    void Send()
    {
        try
        {
            SetSendPacket();

            byte[] sendPacket = StructToByteArray(m_SendPacket);
            m_Client.Send(sendPacket, 0, sendPacket.Length, SocketFlags.None);
        }

        catch (Exception ex)
        {
            //Debug.Log(ex.ToString());
            return;
        }
    }

    void Receive()
    {
        int receive = 0;
        if (m_Client.Available != 0)
        {
            byte[] packet = new byte[1024];

            try
            {
                receive = m_Client.Receive(packet);
            }

            catch (Exception ex)
            {
                //Debug.Log(ex.ToString());
                return;
            }

            m_ReceivePacket = ByteArrayToStruct<ToClientPacket>(packet);

            if (receive > 0)
            {
                DoReceivePacket(); // 받은 값 처리
            }
        }
    }

    void DoReceivePacket()
    {
        Debug.LogFormat($"m_IntArray[0] = {m_ReceivePacket.m_IntArray[0]} " +
           $"m_IntArray[1] = {m_ReceivePacket.m_IntArray[1] } " +
           $"FloatlVariable = {m_ReceivePacket.m_FloatlVariable} " +
           $"StringlVariable = {m_ReceivePacket.m_StringlVariable}" +
           $"BoolVariable = {m_ReceivePacket.m_BoolVariable} " +
           $"IntlVariable = {m_ReceivePacket.m_IntVariable} ");
        //출력: m_IntArray[0] = 7 m_IntArray[1] = 47 FloatlVariable = 2020 StringlVariable = Coder ZeroBoolVariable = True IntlVariable = 13 
    }

    void CloseClient()
    {
        if (m_Client != null)
        {
            m_Client.Close();
            m_Client = null;
        }
    }

    byte[] StructToByteArray(object obj)
    {
        int size = Marshal.SizeOf(obj);
        byte[] arr = new byte[size];
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(obj, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        Marshal.FreeHGlobal(ptr);
        return arr;
    }

    T ByteArrayToStruct<T>(byte[] buffer) where T : struct
    {
        int size = Marshal.SizeOf(typeof(T));
        if (size > buffer.Length)
        {
            throw new Exception();
        }

        IntPtr ptr = Marshal.AllocHGlobal(size);
        Marshal.Copy(buffer, 0, ptr, size);
        T obj = (T)Marshal.PtrToStructure(ptr, typeof(T));
        Marshal.FreeHGlobal(ptr);
        return obj;
    }
}

 

2.3 Packets.cs

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
[Serializable]
public struct ToClientPacket
{
    [MarshalAs(UnmanagedType.Bool)]
    public bool m_BoolVariable;
    public int m_IntVariable;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public int[] m_IntArray;
    public float m_FloatlVariable;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string m_StringlVariable;
}


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
[Serializable]
public struct ToServerPacket
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public int[] m_IntArray;
    public float m_FloatlVariable;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string m_StringlVariable;
    [MarshalAs(UnmanagedType.Bool)]
    public bool m_BoolVariable;
    public int m_IntVariable;
}

 

728x90
반응형