C# 获取UdpClient接收主线程中的数据

C# 获取UdpClient接收主线程中的数据,c#,multithreading,sockets,unity3d,udp,C#,Multithreading,Sockets,Unity3d,Udp,我正在尝试将LAN服务器发现添加到我的Unity游戏中。我正在使用UDP来广播请求。如果网络上的任何人是服务器,他们将使用一些服务器数据进行响应。我已经解决了这一部分,但是我需要使用一些Unity函数(包括其他定制的MonoBehavior脚本)来生成数据包数据 Unity不允许从主线程以外的任何线程访问其API 我正在使用,流。因此,receive AsyncCallback函数(AsyncRequestReceiveData在下面的示例脚本中)在另一个线程中运行,该线程不允许我使用任何Uni

我正在尝试将LAN服务器发现添加到我的Unity游戏中。我正在使用UDP来广播请求。如果网络上的任何人是服务器,他们将使用一些服务器数据进行响应。我已经解决了这一部分,但是我需要使用一些Unity函数(包括其他定制的MonoBehavior脚本)来生成数据包数据

Unity不允许从主线程以外的任何线程访问其API

我正在使用,流。因此,receive AsyncCallback函数(
AsyncRequestReceiveData
在下面的示例脚本中)在另一个线程中运行,该线程不允许我使用任何Unity函数

我使用flamy作为我脚本的基础

我曾尝试在脚本听到请求时在
AsyncRequestReceiveData
中激发委托和事件,但它似乎在同一个单独的线程中激发

由独立线程在主线程中激发的事件将非常有效,但我不确定如何实现这一点


尽管脚本继承自
monobhavior
(Unity 3D中的基本对象),但它应该可以独立运行

脚本的输出应如下所示:

发送请求:请求服务器

收到的请求:RequestingServer

以下是赤裸裸的脚本:

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

public class TestUDP : MonoBehaviour {


    public delegate void RequestReceivedEventHandler(string message);
    public event RequestReceivedEventHandler OnRequestReceived;

    // Use this to trigger the event
    protected virtual void ThisRequestReceived(string message)
    {
        RequestReceivedEventHandler handler = OnRequestReceived;
        if(handler != null)
        {
            handler(message);
        }
    }


    public int requestPort = 55795;

    UdpClient udpRequestSender;
    UdpClient udpRequestReceiver;


    // Use this for initialization
    void Start () {

        // Set up the sender for requests
        this.udpRequestSender = new UdpClient();
        IPEndPoint requestGroupEP = new IPEndPoint(IPAddress.Broadcast, this.requestPort);
        this.udpRequestSender.Connect(requestGroupEP);


        // Set up the receiver for the requests
        // Listen for anyone looking for us
        this.udpRequestReceiver = new UdpClient(this.requestPort);
        this.udpRequestReceiver.BeginReceive(new AsyncCallback(AsyncRequestReceiveData), null);

        // Listen for the request
        this.OnRequestReceived += (message) => {
            Debug.Log("Request Received: " + message);
            // Do some more stuff when we get a request
            // Use `Network.maxConnections` for example
        };

        // Send out the request
        this.SendRequest();
    }

    void Update () {

    }


    void SendRequest()
    {
        try
        {
            string message = "requestingServers";

            if (message != "") 
            {
                Debug.Log("Sendering Request: " + message);
                this.udpRequestSender.Send(System.Text.Encoding.ASCII.GetBytes(message), message.Length);
            }
        }
        catch (ObjectDisposedException e)
        {
            Debug.LogWarning("Trying to send data on already disposed UdpCleint: " + e);
            return;
        }
    }


    void AsyncRequestReceiveData(IAsyncResult result)
    {
        IPEndPoint receiveIPGroup = new IPEndPoint(IPAddress.Any, this.requestPort);
        byte[] received;
        if (this.udpRequestReceiver != null) {
            received = this.udpRequestReceiver.EndReceive(result, ref receiveIPGroup);
        } else {
            return;
        }
        this.udpRequestReceiver.BeginReceive (new AsyncCallback(AsyncRequestReceiveData), null);
        string receivedString = System.Text.Encoding.ASCII.GetString(received);

        // Fire the event
        this.ThisRequestReceived(receivedString);

    }
}


我已经看到了,但我似乎不知道它是如何工作的,以及如何融入其中。

这个代码示例对您有用吗?可以从任何线程调用X

//place in class where main thread object is
void X(int sampleVariable)
{
    if (this.InvokeRequired)
    {
        this.Invoke((MethodInvoker) (() => X(sampleVariable)));
        return;
    }

    // write main thread code here
}

解决方案的工作原理是跟踪需要在主线程中执行的任务列表(队列),然后通过
HandleTasks()在
Update()
中运行该队列;执行并从队列中删除每个队列。您可以通过调用
QueueOnMainThread()
并传递匿名函数来添加到任务队列

我从中得到了一些灵感

您可以使用单独的脚本来管理主线程任务,也可以将其合并到您已经存在的脚本中,您需要在主线程中运行一些东西

以下是我在问题中的原始代码片段的固定解决方案

以下是排队和运行所需的位:

// We use this to keep tasks needed to run in the main thread
private static readonly Queue<Action> tasks = new Queue<Action>();


void Update () {
    this.HandleTasks();
}

void HandleTasks() {
    while (tasks.Count > 0)
    {
        Action task = null;

        lock (tasks)
        {
            if (tasks.Count > 0)
            {
                task = tasks.Dequeue();
            }
        }

        task();
    }
}

public void QueueOnMainThread(Action task)
{
    lock (tasks)
    {
        tasks.Enqueue(task);
    }
}


如果您使用的是WinForms,它有一些很棒的编码模式。

@AgentShark在我的例子中。但这个问题并不完全针对Unity。这不起作用,而且大多数方法、类都在Unity中不可用的
System.Windows.Forms
中。如果我使用WinForms,还有一些更好的替代模式。
this.QueueOnMainThread(() => {
    // We are now in the main thread...
    Debug.Log("maxConnections: " + Network.maxConnections);
});