본문 바로가기

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

[유니티 네트워크] 멀티 스레드 UdpSender, UdpReceiver 구조체 전송 (UdpClient)

728x90
반응형

1. 내용

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

 

포트 번호

내용

0

사용하지 않음.

1 ~1023

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

1024 ~ 49151

등록된 포트(Registered port)

49152 ~ 65535

동적 포트(Dynamic port)

 

 (2) 전송 패키지 사이즈

 Udp는 이론상으로는 65507 byte까지 보낼수 있지만, 내부 통신이 아닌 경우 1024 byte이내를 권장합니다.

 

 (3) 소스 내용

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

  - 클래스 : UdpClient

  - 개체 직렬화 : Marshal

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

 

2. 소스

2.1 Receiver.cs

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

public class Receiver : MonoBehaviour
{
    private UdpClient m_Receiver;    
    public int m_Port = 50001;
    public Packet m_ReceivePacket = new Packet();
    private byte[] m_ReceiveBytes;
    private IPEndPoint m_IpEndPoint;
    private bool m_IsThreading;
    private Thread m_ThrdReceive;

    void Start()
    {
        InitReceiver();
    }

    void OnApplicationQuit()
    {
        CloseReceiver();
    }

    void InitReceiver()
    {
        m_IpEndPoint = new IPEndPoint(IPAddress.Any, m_Port);
        m_Receiver = new UdpClient(m_IpEndPoint);
        m_ThrdReceive = new Thread(ReceivePacket);
        m_IsThreading = true;

        if (m_ThrdReceive.ThreadState != ThreadState.Running)
        {
            m_ThrdReceive.Start();
        }
    }

    void ReceivePacket()
    {
        while (m_IsThreading)
        {
            try
            {
                m_ReceiveBytes = m_Receiver.Receive(ref m_IpEndPoint);
                DoReceive();
            }

            catch (Exception e)
            {
                Debug.Log(e.ToString());
            }
        }
    }

    void DoReceive()
    {
        m_ReceivePacket = ByteArrayToStruct<Packet>(m_ReceiveBytes);

        // 받은 값 처리 ... 
        Debug.LogFormat($"BoolVariable = {m_ReceivePacket.m_BoolVariable} " +
            $"IntlVariable = {m_ReceivePacket.m_IntVariable} " +
            $"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 = True IntlVariable = 2941 m_IntArray[0] = 0 m_IntArray[1] = 1 FloatlVariable = 34.06423 StringlVariable = CoderZero 코더제로 
    }

    void CloseReceiver()
    {
        m_IsThreading = false;
        if (m_Receiver != null)
            m_Receiver.Close();

        if (m_ThrdReceive != null)
        {
            try
            {
                m_ThrdReceive.Abort();
            }
            catch (Exception e)
            {
                Debug.Log(e.ToString());
            }
        }
    }

    T ByteArrayToStruct<T>(byte[] arr)
    {
        object value = null;
        int size = Marshal.SizeOf(typeof(T));
        IntPtr ptr = Marshal.AllocHGlobal(size);

        try
        {
            Marshal.Copy(arr, 0, ptr, size);
            value = Marshal.PtrToStructure(ptr, typeof(T));
        }

        catch (Exception e)
        {
            Debug.Log(e.ToString());
        }

        finally
        {
            Marshal.FreeHGlobal(ptr);
        }

        return (T)value;
    }
}

 

 

2.2 Sender.cs

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

public class Sender : MonoBehaviour
{
    private UdpClient m_Sender;    
    public string m_Ip = "127.0.0.1";
    public int m_Port = 50001;
    public Packet m_SendPacket = new Packet();
    private IPEndPoint m_IpEndPoint;
    private byte[] m_SendBytes;
    private bool m_IsThreading;
    private Thread m_ThrdSend;

    void Start()
    {
        InitSender(m_Ip, m_Port);
    }

    void Update()
    {
        SetSendPacket();
    }

    void OnApplicationQuit()
    {
        CloseSender();
    }

    void InitSender(string ip, int port)
    {
        m_IpEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);

        m_Sender = new UdpClient();
        m_Sender.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        m_Sender.Connect(m_IpEndPoint);

        m_ThrdSend = new Thread(ThrdSendPacket);
        m_IsThreading = true;


        if (m_ThrdSend.ThreadState != System.Threading.ThreadState.Running)
        {
            m_ThrdSend.Start();
        }
    }

    void ThrdSendPacket()
    {
        while (m_IsThreading)
        {
            Thread.Sleep(16);
            try
            {
                m_Sender.Send(m_SendBytes, m_SendBytes.Length);
            }

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

    void CloseSender()
    {
        m_IsThreading = false;

        if (m_Sender != null)
            m_Sender.Close();

        if (m_ThrdSend != null)
        {
            try
            {
                m_ThrdSend.Abort();
            }

            catch (Exception e)
            {
                Debug.Log(e.ToString());
            }
        }
    }

    void SetSendPacket()
    {
        m_SendPacket.m_BoolVariable = !m_SendPacket.m_BoolVariable;
        m_SendPacket.m_IntVariable++;
        if (m_SendPacket.m_IntVariable >= int.MaxValue - 1)
            m_SendPacket.m_IntVariable = default(int);

        m_SendPacket.m_IntArray = new int[2];
        m_SendPacket.m_IntArray[0] = 0;
        m_SendPacket.m_IntArray[1] = 1;
        m_SendPacket.m_FloatlVariable = UnityEngine.Random.Range(0f, 100.0f);
        m_SendPacket.m_StringlVariable = "CoderZero 코더제로";

        m_SendBytes = StructToByteArray(m_SendPacket);
    }

    byte[] StructToByteArray(object obj)
    {
        int datasize = Marshal.SizeOf(obj);
        IntPtr buff = Marshal.AllocHGlobal(datasize);
        Marshal.StructureToPtr(obj, buff, false);
        byte[] data = new byte[datasize];
        Marshal.Copy(buff, data, 0, datasize);
        Marshal.FreeHGlobal(buff);
        return data;
    }
}

 

2.3 Packets.cs

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
[Serializable]
public struct Packet
{
    [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;
}

 

728x90
반응형