获取UdpClient接收返回的数据在主线程 [英] Getting UdpClient Receive data back in the Main thread

查看:251
本文介绍了获取UdpClient接收返回的数据在主线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图LAN服务器发现添加到我的统一的游戏。我使用UDP广播请求。如果网络上的任何人是一个服务器,他们将与一些服务器数据作出响应。我有这部分想通了,但我需要使用一些统一的功能(包括其他自定义monobehaviour脚本)来生成分组数据。

I am trying to add LAN server discovery to my Unity game. I am using UDP to broadcast requests. If anyone on the network is a server, they will respond with some server data. I have this part figured out, but I need to use some Unity functions(which include other custom monobehaviour scripts) to generate the packet data.

统一将不允许访问它的API从除主之外的任何线索。

Unity will not allow access to its APIs from any thread other than the main one.

我使用的 UdpClient.BeginReceive ,的 UdpClient.EndReceive ,的的AsyncCallback 流。所以收到的AsyncCallback功能( AsyncRequestReceiveData 在下面的示例脚本)在另一个线程不允许我使用任何功能的统一运行。

I am using the UdpClient.BeginReceive, UdpClient.EndReceive, AsyncCallback flow. So the receive AsyncCallback function(AsyncRequestReceiveData in the example script below) is run in another thread which doesn't allow me to use any Unity functions.

我用这个答案以炽烈的为我的脚本的基础。

I used this answer by flamy as a basis for my script.

我试图做一个委托和事件的 AsyncRequestReceiveData 当脚本听到的请求,但它似乎在同一个单独的线程开火。

I have tried making a delegate and event to fire in AsyncRequestReceiveData when the script hears a request, but it seems to fire in the same separate thread.

由单独的线程在主线程中触发的事件会工作的伟大,但我不知道如何可以这样做。

An event fired in the main thread by the separate thread would work great, but I am not sure on how this could be done.

虽然脚本 MonoBehaviour 继承(在Unity 3D基础对象),它应该能够正常运行独立

Although the script inherits from MonoBehaviour (base object in Unity 3D) it should run just fine standalone.

脚本的输出应该是这样的:

The output of the script should look like:

Sendering请求: requestingServers

Sendering Request: requestingServers

请求收稿日期:requestingServers

Request Received: requestingServers

下面是准系统脚本:

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

    }
}






我已经看到这个问题(的清洁的方式来调用跨-Thread活动的),但我似乎无法弄清楚它是如何来工作的,以及如何在收纳。


I have seen this question (Cleanest Way to Invoke Cross-Thread Events) but I can't seem to figure out how to it works and how to incorporate in.

推荐答案

解决方案的工作通过保持需要在主线程中执行的任务(队列)的列表中的曲目,然后通过在更新该队列()通过 HandleTasks();执行并从队列中删除各一个。你可以通过调用 QueueOnMainThread()并传递一个匿名函数添加到任务队列中。

The solution works by keeping track of a list of tasks(a queue) that need to be executed in the main thread, and then run through that queue in the Update() via HandleTasks(); executing and removing each one from the queue. You can add to the task queue by calling QueueOnMainThread() and passing an anonymous function.

我得到了一些灵​​感从此答案由 Pragmateek <的/ A>

I got some inspiration from this answer by Pragmateek

您可以有一个单独的脚本来管理主线任务或者只是把它纳入你已有的,你需要运行在主线程中的东西的脚本。

You can have a separate script to manage main thread tasks or just incorporate it into your already existing script where you need to run something in the main thread.

下面是完整的源,固定解我原来的,片段中的问题。

Here is the full source, fixed solution to my original snippet in the question.

下面就是必需的位得到排队和运行:

Below is just the necessary bits to get the queuing up and running:

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

要排队的东西了在主线程只是通过它lambda表达式:

To Queue something up on the main thread just pass it a lambda expression:

this.QueueOnMainThread(() => {
    // We are now in the main thread...
    Debug.Log("maxConnections: " + Network.maxConnections);
});






如果您正在使用的WinForms,的这篇文章:避免InvokeRequired 有一些伟大的编码模式。


If you are using WinForms, this article: Avoiding InvokeRequired has some great coding patterns.

这篇关于获取UdpClient接收返回的数据在主线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆