Казалось бы, UdpClient.BeginReceive()
и UdpClient.EndReceive()
плохо реализованы / поняты.И, конечно, по сравнению с тем, как реализован TcpListener, использовать его намного сложнее.
Есть несколько вещей, которые вы можете сделать, чтобы UdpClient.Receive()
работал лучше для вас.Во-первых, установка тайм-аутов на базовом сокете Client позволит провалу управления (исключение), что позволит продолжить или зациклить поток управления.Во-вторых, создавая прослушиватель UDP в новом потоке (создание которого я не показывал), вы можете избежать эффекта полублокировки функции UdpClient.Receive()
и эффективно отменить этот поток позже, если вы сделаете это правильно.
Код ниже состоит из трех частей.Первая и последняя части должны быть в вашем главном цикле в точках входа и выхода соответственно.Вторая часть должна быть в новой созданной вами теме.
Простой пример:
// Define this globally, on your main thread
UdpClient listener = null;
// ...
// ...
// Create a new thread and run this code:
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9999);
byte[] data = new byte[0];
string message = "";
listener.Client.SendTimeout = 5000;
listener.Client.ReceiveTimeout = 5000;
listener = new UdpClient(endPoint);
while(true)
{
try
{
data = listener.Receive(ref endPoint);
message = Encoding.ASCII.GetString(data);
}
catch(System.Net.Socket.SocketException ex)
{
if (ex.ErrorCode != 10060)
{
// Handle the error. 10060 is a timeout error, which is expected.
}
}
// Do something else here.
// ...
//
// If your process is eating CPU, you may want to sleep briefly
// System.Threading.Thread.Sleep(10);
}
// ...
// ...
// Back on your main thread, when it's exiting, run this code
// in order to completely kill off the UDP thread you created above:
listener.Close();
thread.Close();
thread.Abort();
thread.Join(5000);
thread = null;
В дополнение ко всему этому вы также можете проверить UdpClient.Available > 0
, чтобы определить, является лилюбые запросы UDP ставятся в очередь перед выполнением UdpClient.Receive()
- это полностью устраняет аспект блокировки.Я советую вам попробовать это с осторожностью, так как это поведение не отображается в документации Microsoft, но, похоже, работает.
Примечание:
MSDN exmaple code Вы, возможно, обнаружили, что при исследовании этой проблемы требуется дополнительный определенный пользователем класс - UdpState.Это не класс библиотеки .NET.Это, кажется, сбивает с толку многих людей, когда они исследуют эту проблему.
таймауты не обязательно должны быть установлены, чтобы позволить вашему приложению полностью завершиться, но они позволят вамделайте другие вещи в этом цикле, а не блокируйте навсегда.
Команда listener.Close () важна, потому что она заставляет UdpClient генерировать исключение и выходить из цикла, позволяя Thread.Abort() чтобы быть обработанным.Без этого вы не сможете корректно завершить работу потока слушателя, пока не истечет время ожидания или не будет получен пакет UDP, в результате чего код будет продолжаться после блока UdpClient.Receive ().
Просто добавьтена этот бесценный ответ вот рабочий и проверенный фрагмент кода.(Здесь, в контексте Unity3D, но, конечно, для любого c #.)
// minmal flawless UDP listener per PretorianNZ
using System.Collections;
using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;
void Start()
{
listenThread = new Thread (new ThreadStart (SimplestReceiver));
listenThread.Start();
}
private Thread listenThread;
private UdpClient listenClient;
private void SimplestReceiver()
{
Debug.Log(",,,,,,,,,,,, Overall listener thread started.");
IPEndPoint listenEndPoint = new IPEndPoint(IPAddress.Any, 1260);
listenClient = new UdpClient(listenEndPoint);
Debug.Log(",,,,,,,,,,,, listen client started.");
while(true)
{
Debug.Log(",,,,, listen client listening");
try
{
Byte[] data = listenClient.Receive(ref listenEndPoint);
string message = Encoding.ASCII.GetString(data);
Debug.Log("Listener heard: " +message);
}
catch( SocketException ex)
{
if (ex.ErrorCode != 10060)
Debug.Log("a more serious error " +ex.ErrorCode);
else
Debug.Log("expected timeout error");
}
Thread.Sleep(10); // tune for your situation, can usually be omitted
}
}
void OnDestroy() { CleanUp(); }
void OnDisable() { CleanUp(); }
// be certain to catch ALL possibilities of exit in your environment,
// or else the thread will typically live on beyond the app quitting.
void CleanUp()
{
Debug.Log ("Cleanup for listener...");
// note, consider carefully that it may not be running
listenClient.Close();
Debug.Log(",,,,, listen client correctly stopped");
listenThread.Abort();
listenThread.Join(5000);
listenThread = null;
Debug.Log(",,,,, listener thread correctly stopped");
}