Home > Software design >  Listen to message from an IP from another machine
Listen to message from an IP from another machine

Time:05-04

I am trying to send a message to Unity through UDP. The machine that sends the message has IP as 192.16.14.1 and port as 3034. How do I enter these two inside of Unity application? I have found a code to listen for UDP messages but I cannot set the IP address here. Also the Unity application should be running at all times even if the message from another machine is sent or not.

using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

public class UDP_Listen : MonoBehaviour
{
    UdpClient clientData;
    int portData = 3034;
    public int receiveBufferSize = 120000;

    public bool showDebug = false;
    IPEndPoint ipEndPointData;
    private object obj = null;
    private System.AsyncCallback AC;
    byte[] receivedBytes;

    void Start()
    {
        InitializeUDPListener();
    }
    public void InitializeUDPListener()
    {
        ipEndPointData = new IPEndPoint(IPAddress.Any, portData);
        clientData = new UdpClient();
        clientData.Client.ReceiveBufferSize = receiveBufferSize;
        clientData.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true);
        clientData.ExclusiveAddressUse = false;
        clientData.EnableBroadcast = true;
        clientData.Client.Bind(ipEndPointData);
        clientData.DontFragment = true;
        if (showDebug) Debug.Log("BufSize: "   clientData.Client.ReceiveBufferSize);
        AC = new System.AsyncCallback(ReceivedUDPPacket);
        clientData.BeginReceive(AC, obj);
        Debug.Log("UDP - Start Receiving..");
    }

    void ReceivedUDPPacket(System.IAsyncResult result)
    {
        //stopwatch.Start();
        receivedBytes = clientData.EndReceive(result, ref ipEndPointData);

        ParsePacket();

        clientData.BeginReceive(AC, obj);

        //stopwatch.Stop();
        //Debug.Log(stopwatch.ElapsedTicks);
        //stopwatch.Reset();
    } // ReceiveCallBack

    void ParsePacket()
    {
        // work with receivedBytes
        Debug.Log("receivedBytes len = "   receivedBytes.Length);
    }

    void OnDestroy()
    {
        if (clientData != null)
        {
            clientData.Close();
        }

    }
}

CodePudding user response:

So there are two different things:

  • You want to define the receiving local port you Bind your socket to
  • You want to define the expected sending remote ip port you want to Receive from

Currently you are using the very same one

ipEndPointData = new IPEndPoint(IPAddress.Any, portData);

for both! (Fun fact: As a side effect by using always the same field you basically allow any sender but are then bond to that specific sender from this moment on)

Actually a lot of things you configure there are the default values anyway so here is more or less what I would do

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

public class UDP_Listen : MonoBehaviour
{
    public ushort localReceiverPort = 3034;
    
    public string senderIP = "192.168.111.1";
    public ushort remoteSenderPort = 3034;

    public bool showDebug = false;

    // Thread-safe Queue to handle enqueued actions in the Unity main thread
    private readonly ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();

    private Thread udpListenerThread;
    
    private void Start()
    {
        // do your things completely asynchronous in a background thread
        udpListenerThread = new Thread(UDPListenerThread);
        udpListenerThread.Start();
    }

    private void Update()
    {
        // in the Unity main thread work off the actions
        while (mainThreadActions.TryDequeue(out var action))
        {
            action?.Invoke();
        }
    }

    private void UDPListenerThread()
    {
        UdpClient udpClient = null;

        try
        { 
            // local end point listens on any local IP
            var localEndpoint = new IPEndPoint(IPAddress.Any, localReceiverPort);
            udpClient = new UdpClient(localEndpoint);
            
            if (showDebug)
            {
                Debug.Log("BufSize: "   clientData.Client.ReceiveBufferSize);
            }

            Debug.Log("UDP - Start Receiving..");

            // endless loop -> ok since in a thread and containing blocking call(s)
            while (true)
            {
                // remote sender endpoint -> listens only to specific IP
                var expectedSenderEndpoint = new IPEndPoint(IPAddress.Parse(senderIP), remoteSenderPort);
                
                // blocking call - but doesn't matter since this is a thread
                var receivedBytes = udpClient.Receive(ref expectedSenderEndpoint);

                // parse the bytes here
                // do any expensive work while still on a background thread
                
                mainThreadActions.Enqueue(() =>
                {
                    // Put anything in here that is required to happen in the Unity main thread
                    // so basically anything using GameObject, Transform, etc 
                });
            }
        }
        // thrown for "Abort"
        catch (ThreadAbortException)
        {
            Debug.Log("UDP Listener terminated");
        }
        // Catch but Log any other exception
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        // This is run even if an exception happend
        finally
        {
            // either way dispose the UDP client
            udpClient?.Dispose();
        }
    }

    private void OnDestroy()
    {
        udpListenerThread?.Abort();
    }
}

I'm sure the same can be done also using the BeginReceive/EndReceive or task based alternatives but since it is going to run endless anyway I personally find a thread often easier to read and maintain.

CodePudding user response:

I think you got it backwards. This code you shared is for, like you said, listen UDP protocol on desired port. This piece of code needs to be inside your "server". By server try to understand that as the receiving side.

on your shared method InitializeUDPListener(); we have this piece:

ipEndPointData = new IPEndPoint(IPAddress.Any, portData);

this means you are initializing your udp socket to listen for ANY ip adresses at the given port. That said, you have your server ready to go, what you need to do is setup the client side, the one who sends the message.

here some example:

    public string serverIp = "127.0.0.1"; // your server ip, this one is sending to local host 
public int serverPort = 28500; // your server port



public void ClientSendMessage()
{
    Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

    IPAddress broadcast = IPAddress.Parse(serverIp);

    byte[] sendbuf = Encoding.ASCII.GetBytes("THIS IS A MESSAGE FROM CLIENT!");

    IPEndPoint ep = new IPEndPoint(broadcast, serverPort);

    s.SendTo(sendbuf, ep);


}

I encourage you to read about UDP/TCP protocols before using them. MS has documentation with details.

here some links:

TCP

UDP

Sockets

  • Related