gpt4 book ai didi

c# - 在主线程中获取 UdpClient 接收数据

转载 作者:行者123 更新时间:2023-12-03 12:05:18 25 4
gpt4 key购买 nike

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

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

我正在使用 UdpClient.BeginReceive , UdpClient.EndReceive , AsyncCallback流动。所以接收 AsyncCallback 函数(下面示例脚本中的 AsyncRequestReceiveData)在另一个线程中运行,它不允许我使用任何 Unity 函数。

我用了this answer由 flamy 作为我剧本的基础。

我已尝试在 AsyncRequestReceiveData 中触发委托(delegate)和事件当脚本听到请求时,它似乎在同一个单独的线程中触发。

由单独线程在主线程中触发的事件会很好用,但我不确定如何做到这一点。

虽然脚本继承自 MonoBehaviour (Unity 3D 中的基础对象)它应该可以独立运行。

脚本的输出应如下所示:

Sendering Request: 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);

}
}

我看过 this question (Cleanest Way to Invoke Cross-Thread Events)但我似乎无法弄清楚它是如何工作的以及如何融入其中。

最佳答案

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

我从 this answer 获得了一些灵感由 Pragmateek

您可以有一个单独的脚本来管理主线程任务,或者只是将其合并到您需要在主线程中运行某些东西的现有脚本中。

这里是 full source ,修复了我在问题中的原始片段的解决方案。

以下只是启动和运行队列的必要位:

// 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 表达式:
this.QueueOnMainThread(() => {
// We are now in the main thread...
Debug.Log("maxConnections: " + Network.maxConnections);
});

如果您使用的是 WinForms, this article: Avoiding InvokeRequired有一些很棒的编码模式。

关于c# - 在主线程中获取 UdpClient 接收数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23588443/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com