从另一台机器的 IP 收听消息

Listen to message from an IP from another machine

我正在尝试通过 UDP 向 Unity 发送消息。发送消息的机器 IP 为 192.16.14.1,端口为 3034。如何在 Unity 应用程序中输入这两个内容?我找到了侦听 UDP 消息的代码,但我无法在此处设置 IP 地址。此外,Unity 应用程序应始终 运行,即使是否发送来自另一台机器的消息也是如此。

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();
        }

    }
}

我想你搞反了。正如您所说,您共享的这段代码用于在所需端口上侦听 UDP 协议。这段代码需要在您的“服务器”中。通过服务器尝试理解为接收方。

在您的共享方法 InitializeUDPListener() 上;我们有这个:

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

这意味着您正在初始化您的 udp 套接字以侦听给定端口上的任何 ip 地址。也就是说,您的服务器已准备就绪,您需要做的是设置客户端,即发送消息的客户端。

这里有一些例子:

    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);


}

我鼓励您在使用协议之前阅读 UDP/TCP 协议。 MS 有详细的文档。

这里有一些链接:

TCP

UDP

Sockets

所以有两个不同的东西:

  • 你想定义 接收本地端口你Bind你的套接字到
  • 您想要定义预期的发送远程 ip + 端口您想要Receive来自

目前您使用的是同一个

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

两者兼得! (有趣的事实:作为副作用,通过始终使用相同的字段,您基本上允许 任何 发件人,但从这一刻起绑定到该特定发件人)

实际上你配置的很多东西都有默认值所以这里或多或少是我会做的

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();
    }
}

我敢肯定,使用 BeginReceive/EndReceive 或基于任务的替代方案也可以完成同样的工作,但由于它会 运行 无休止,所以我个人找到了一个线程通常更易于阅读和维护。

如果 Unity 应用程序要不断接收消息,它需要类似于:

UdpClient listener = new UdpClient(11000);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("192.16.14.1"), 3034);

while (true)
{
    byte[] bytes = listener.Receive(ref groupEP);
}

这应该只读取来自特定 IP 的调用,不确定您希望 UDPClient 从哪个端口读取(在 UDPClient 构造函数中指定),但您可以将其设置为您需要的任何值。