본문 바로가기

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

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

728x90
반응형

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 UnityEngine;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System;
using System.Runtime.InteropServices;

public class Server : MonoBehaviour
{
    private Socket m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    public int m_Port = 50001;
    public ToServerPacket m_ReceivePacket = new ToServerPacket();
    public ToClientPacket m_SendPacket = new ToClientPacket();
    private List<Socket>m_Clients = new List<Socket>();

    void Awake()
    {
        InitServer();
    }

    void InitServer()
    {
        IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, m_Port);

        m_Socket.Bind(ipEndPoint);
        m_Socket.Listen(100);

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

    void Start()
    {
        Debug.Log("클라이언트들을 기다립니다...");
    }

    void Update()
    {
        Accept();
        CheckClientList();

        if (m_Clients.Count > 0)
        {
            Receive();
            Send();
        }
    }

    void OnApplicationQuit()
    {
        CloseServer();
    }

    void Accept()
    {
        List<Socket> clinets = new List<Socket>();
        clinets.Add(m_Socket);
        Socket.Select(clinets, null, null, 1000);

        for (int i = 0; i < clinets.Count; i++)
        {
            Socket newSocket = ((Socket)clinets[i]).Accept();
            m_Clients.Add(newSocket);
            Debug.Log("Did connect");
        }
    }

    void CheckClientList()
    {
        for (int i = 0; i < m_Clients.Count; i++)
        {
            if (!IsConnected(m_Clients[i]))
            {
                m_Clients.RemoveAt(i);
            }
        }
    }

    bool IsConnected(Socket socket)
    {
        try
        {
            if (socket != null && socket != null && socket.Connected)
            {
                if (socket.Poll(0, SelectMode.SelectRead))
                {
                    return !(socket.Receive(new byte[1], SocketFlags.Peek) == 0);
                }
                return true;
            }
            return false;
        }

        catch
        {
            return false;
        }
    }
    
    void Receive()
    {
        for (int i = 0; i < m_Clients.Count; i++)
        {
            byte[] packet = new byte[1024];
            int receive = m_Clients[i].Receive(packet);
            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()
    {
        for (int i = 0; i < m_Clients.Count; i++)
        {
            try
            {
                SetSendPacket();

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

            catch (Exception ex)
            {
                Debug.Log(ex);
                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_Socket != null)
            m_Socket.Close();

        m_Socket = null;

        for (int i = 0; i < m_Clients.Count; i++)
        {
            m_Clients[i].Close();
        }

        m_Clients.Clear();
    }
      
    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
{
    public string m_Ip = "127.0.0.1";
    public int m_Port = 50001;
    private IPEndPoint m_IpEndPoint;
    public ToServerPacket m_SendPacket = new ToServerPacket();
    public  ToClientPacket m_ReceivePacket = new ToClientPacket();
    private bool m_IsConnected;
    private Socket m_Socket;

    void Awake()
    {
        InitSocket();

        if (!m_IsConnected)
            ConnectServer();
    }

    void OnApplicationQuit()
    {
        CloseSocket();
    }

    void Update()
    {
        if (!m_IsConnected)
            ConnectServer();

        else
        {           
            Send(); 
            Receive();
        }
    }

    void InitSocket()
    {
        m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPAddress ipAddress = IPAddress.Parse(m_Ip);
        m_IpEndPoint = new IPEndPoint(ipAddress, m_Port);

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

    void ConnectServer()
    {
        try
        {
            m_Socket.Connect(m_IpEndPoint);
            m_IsConnected = true;
        }

        catch (SocketException ex)
        {
            //Debug.Log(ex.Message);
        }
    }

    void CloseSocket()
    {
        m_Socket.Close();
        m_Socket = null;
    }

    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 Receive()
    {
        int receive = 0;

        try
        {
            byte[] receivedbytes = new byte[512];
            receive = m_Socket.Receive(receivedbytes);

            m_ReceivePacket = ByteArrayToStruct<ToClientPacket>(receivedbytes);
        }

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

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

    void DoReceivePacket()
    {
        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 = 13 m_IntArray[0] = 7 m_IntArray[1] = 47 FloatlVariable = 2020 StringlVariable = Coder Zero
    }

    void Send()
    {
        try
        {
            SetSendPacket();
            byte[] sendPacket = StructToByteArray(m_SendPacket);
            m_Socket.Send(sendPacket);
        }

        catch (SocketException ex)
        {
            Debug.Log(ex.Message);
        }
    }

    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
반응형