반응형

UDP(User Datagram Protocol)는 TCP와 같이 IP에 기반한 전송 프로토콜이다.

TCP는 송수신 전에 반드시 서버-클라이언트 연결이 전제되어야 하는 반면, UDP는 별도의 연결이 필요없다.


유니티에서 UDP를 사용하기 위해서는 System.Net.Sockets 네임스페이스 안의 UdpClient 클래스Socket 클래스를 사용한다. TCP와 달리 UDP는 별도의 UDP 서버 클래스가 없으며, 서버도 UdpClient 클래스를 사용한다.

 

 

*UDP 클라이언트 구현

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using static System.Console;



(1) UdpClient 객체 성성

UDP 통신을 위해 System.Net.Sockets 네임스페이스의 UdpClient 객체를 생성한다. 데이터를 내보낼 때 서버와 포트가 필요한데, UdpClient 생성자에서 잡아주는 경우도 있지만, 만약 하나의 UdpClient 객체로 여러 서버에 데이터를 보낼 경우는 Send() 메서드에서 서버와 포트를 지정해준다. 

UdpClient cli = new UdpClient();



(2) 데이터 송신

UdpClient 객체의 Send() 메서드를 사용하여 데이터를  IP와 포트번호에 맞게 보낸다. 네트워크 데이터 송수신은 바이트 데이터를 사용하기 때문에, 문자열을 보낼 경우 먼저 바이트로 인코딩한 후 보내게 된다.

msg = "안녕하세요";
byte[] datagram = Encoding.UTF8.GetBytes(msg);
cli.Send(datagram, datagram.Length, "127.0.0.1", 7777);
WriteLine("[Send] 127.0.0.1:7777 로 {0} 바이트 전송", datagram.Length);



(3) 데이터 수신

UDP에서 데이타를 수신할 경우는 UdpClient 객체의 Receive() 메서드를 사용한다. Receive() 메서드는 특히 수신 데이타와 함께 상대 컴퓨터의 종단점(IP주소와 포트) 정보도 같이 전달받는데, 이를 위해 IPEndPoint 객체를 ref 파라미터로 전달한다. 이를 통해 데이타가 수신되면 누가 그 데이타를 전송했는지 알 수 있다. 

IPEndPoint epRemote = new IPEndPoint(IPAddress.Any, 0);
byte[] bytes = cli.Receive(ref epRemote);
WriteLine("[Receive] {0} 로부터 {1} 바이트 수신", epRemote.ToString(), bytes.Length);

 

 

(4) UdpClient 객체 닫기

마지막으로 UdpClient 객체를 닫는다.

cli.Close();

 

 

*UDP 서버 구현

 

UDP 서버는 포트를 열고 클라이언트로부터 들어오는 데이타그램을 수신하게 된다. 즉, UDP 서버는 통상 UDP 포트를 Listening하고 있으면서 루프 안에서 계속 데이타 송수신을 처리하는 형태로 구현된다. UDP 클라이언트로부터 데이타그램을 직접 받아 처리하면 된다. UDP 서버는 UDP 클라이언트와 같이 거의 동일한 기능을 갖기 때문에 별도의 UDP 서버 클래스가 없고 UdpClient 클래스를 사용한다.

using System;
using System.Net;
using System.Net.Sockets;

 

 

(1) UdpClient 객체 성성

UDP 클라이언트로부터 데이타를 받아들이기 위해 먼저 Listening할 포트를 지정하며 UdpClient 객체를 생성한다.

// 포트 7777 에서 Listening
UdpClient srv = new UdpClient(7777);



(2) 데이터 수신

UDP 에서 데이타를 수신하기 위해 UdpClient 객체의 Receive() 메서드를 사용한다. Receive() 메서드는 특히 수신 데이타와 함께 상대 UDP 클라이언트 종단점(IP주소와 포트) 정보도 같이 전달받는데, 이를 위해 IPEndPoint 객체를 ref 파라미터로 전달한다. 데이타 수신 후, ref 파라미터를 체크하면 데이타를 보낸 UDP 클라이언트의 IP 주소와 포트를 알 수 있다.

// 클라이언트 IP를 담을 변수
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);

while(true) {
    byte[] dgram = srv.Receive(ref remoteEP);
    Console.WriteLine("[Receive] {0} 로부터 {1} 바이트 수신", remoteEP.ToString(), dgram.Length);
}

 

 

(3) 데이타 송신

UdpClient 객체의 Send() 메서드를 사용하여 데이타를 UDP 클라이언트로 전달한다. 이 때 클라이언트 IP는 위의 Receive() 메서드에서 받아온 IP 주소를 사용한다.

srv.Send(dgram, dgram.Length, remoteEP);
Console.WriteLine("[Send] {0} 로 {1} 바이트 송신", remoteEP.ToString(), dgram.Length);

 

 

*샘플 스크립트

 

udp_connector.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;


public class UDPConnector : MonoBehaviour {
    UdpClient ReceivePort, SendPort;
    IPEndPoint remoteEndPoint;

    public class MyUDPEvent : UnityEvent<string> { }
    public MyUDPEvent OnReceiveMessage = new MyUDPEvent ();


    void Start () {
        OnReceiveMessage.AddListener (receiveMsg);
    }

    void Init () {
        ReceivePort = new UdpClient (Int32.Parse (IF_ReceivePort.text));
        ReceivePort.BeginReceive (OnReceive, null);
        Send ();
    }

    public void Send (string msg = "receive from Unity") {
        SendPort = new UdpClient ();

        string remoteIP = IF_SendIP.text;
        int remotePort = Int32.Parse (IF_SendPort.text);
        remoteEndPoint = new IPEndPoint (IPAddress.Parse (remoteIP), remotePort);

        byte[] data = System.Text.Encoding.UTF8.GetBytes (msg);
        SendPort.Send (data, data.Length, remoteEndPoint);
    }

    void OnReceive (IAsyncResult ar) {
        try {
            IPEndPoint ipEndPoint = null;
            byte[] data = ReceivePort.EndReceive (ar, ref ipEndPoint);
            message = System.Text.Encoding.UTF8.GetString (data);
        } catch (SocketException e) { }

        ReceivePort.BeginReceive (OnReceive, null);
    }

    void receiveMsg (string msg) {
        Txt_Message.text = Txt_Message.text + msg + "\n";
        // Debug.Log(message);
    }

    void Update () {
        OnReceiveMessage.Invoke (message);
    }

    public void ShutDown () {
        OnReceiveMessage.RemoveAllListeners ();
        if (ReceivePort != null)
            ReceivePort.Close ();
        if (SendPort != null)
            SendPort.Close ();
        ReceivePort = null;
        SendPort = null;
    }
}

 

communication_handler.cs

public class CommunicationHandler : MonoBehaviour {
    public UDPConnector UDP;

    void Start () {
        UDP.OnReceiveMessage.AddListener (ReceiveMsg);
    }

    void ReceiveMsg (string msg) {
    
    }

    public void SendMsg (string msg) {
        UDP.Send (msg);
    }

    void OnApplicationQuit () {
        UDP.ShutDown ();
    }
}

 

반응형

'unity C#' 카테고리의 다른 글

[Unity] Tetris 게임 만들기  (1) 2021.06.25
[Unity] 스도쿠게임 만들기  (0) 2021.06.15
[Unity] Partial class  (0) 2021.05.04
[Unity] Sprite Atlas  (0) 2021.04.13
[Unity] vscode .net framework C# 에러문제  (1) 2020.08.28

+ Recent posts