【问题标题】:Start/Stop receiving data on UDP port on a timed interval以定时间隔开始/停止在 UDP 端口上接收数据
【发布时间】:2019-05-29 22:41:42
【问题描述】:

我有一个函数可以监听特定端口上的 UDP 数据包。每当我收到数据时,我希望函数停止在该端口上接收数据,等待 5 秒,然后继续侦听数据。基本流程是:

  1. 监听 UDP 数据包。
  2. 接收 UDP 数据包并对该数据进行任何必要的处理。
  3. 阻止传入的 UDP 数据包并等待 5 秒。
  4. 重复。

我的功能正常工作,但我不知道如何在 5 秒的等待期内阻止 UDP 数据包。我在UdpClient 上使用了EnableBroadcast 布尔值,但它什么也没做。我还尝试将BroadcastBlockSourceSocketOptionName 设置为true/false,但它什么也没做。

private async void ReceiveUdpPackets()
{
    await Task.Run(async () =>
    {
        try
        {
            while (receivePackets)
            {
                Log.Info("Listening for UDP packets...");

                listener.Client.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.BlockSource, false);
                byte[] bytes = listener.Receive(ref groupEP);
                listener.Client.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.BlockSource, true);

                Log.Info($"Received UDP broadcast from {groupEP} :");
                Log.Info($"{Encoding.ASCII.GetString(bytes, 0, bytes.Length)}");

                var bString = BitConverter.ToString(bytes).Replace("-", "");
                Log.Info("UDP ByteString: " + bString);

                DecodeByteString(bString);
                await Task.Delay(5000);
            }
        }
        catch (Exception ex)
        {
            Log.Error("There was an issue decoding the UDP packet: " + ex.ToString());
        }
    });
}

【问题讨论】:

  • SocketOptionName.BlockSource 不是SocketOptionLevel.Udp 的有效选项。它用于SocketOptionLevel.IP,而不是管理多播成员资格。使 UDP 套接字停止侦听数据包的唯一方法是关闭套接字。否则,我建议将套接字移动到另一个线程,或使用异步读取,在这 5 秒内监视并丢弃任何新数据,然后您可以在准备好时检索套接字或取消读取。
  • 您关心传入的 UDP 数据报会发生什么吗?你能简单地阅读它们而不用它们做任何事情...... 5 秒钟?还是您希望远程对等方在尝试发送它们时失败?
  • 个人来说,我只用signalr,它会更容易设置,更容易维护,更容易做对

标签: c# sockets udp dispose


【解决方案1】:

如果您希望客户端在等待期间无法发送任何内容,则需要某种方式让他们知道服务器未处于侦听状态。您可以在不同的端口上运行单独的任务,客户端可以查询以查看是否可以在主端口上传输和处理数据。有很多方法可以解决这个问题,但基本思想听起来很简单:客户端查询辅助端口,服务器以指示主端口是否“在线”的消息进行响应。如果没有,客户端会一直等待,直到它收到来自服务器的继续。如果是这样,客户端可以立即将其信息发送过来。

如果这听起来不吸引人,您还可以在服务器端实现数据队列模型。您可以让一个任务使用来自客户端的传入数据填充BlockingCollection,并让另一个线程处理从该源提取的数据。请注意,这不会停止传输,因此它不是您问题的完美解决方案,但它确实有几个好处。这是代码可能看起来像的(有缺陷的)示例:

using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace ExampleNamespace
{

    /* 
     * data class; encapsulates byte array data received from a client. 
     * in an actual implementation this might be a nice spot to put code 
     * that interprets bytes from the client in some way
     */
    public class Transmission
    {
        public IPEndPoint Client { get; set; }

        public byte[] Data { get; set; }

        public Transmission(IPEndPoint client, byte[] data)
        {
            Client = client;
            Data = data;
        }
    }

    public class Server
    {
        private UdpClient listener;

        /*
         * you probably want to make this a bounded collection to prevent the
         * collection from getting too big
         */
        private BlockingCollection<Transmission> dataReceived = new BlockingCollection<Transmission>();

        private bool receivePackets = true;

        private bool processData = true;

        private IPEndPoint groupEP;

        public Server(IPEndPoint listenAt)
        {
            listener = new UdpClient(listenAt);
            groupEP = listenAt;
        }

        public void Run()
        {
            ProcessData();
            ReceiveData();
        }

        public async void ReceiveData()
        {
            await Task.Run(() => {
                while (receivePackets)
                {
                    byte[] data = listener.Receive(ref groupEP);
                    dataReceived.Add(new Transmission(groupEP, data));
                }
            });
        }

        public async void ProcessData()
        {
            //you could run just a few of these to increase your processing 
            //speed without changing how things work
            await Task.Run(async () => {
                while (processData)
                {
                    //blocks until data is available
                    Transmission data = dataReceived.Take();

                    //do things with the data...

                    await Task.Delay(5000);
                }
            });
        }
    }
}

如前所述,这个例子有一些问题——如果你想真正实现它,我强烈建议你考虑将dataReceived 设置为有界阻塞集合,这样你就不必担心内存问题。此外,通过将CancellationToken 传递给您的 Take() 和 TryTake() 调用以及您创建的任何线程,能够关闭整个进程通常很有用。但是,设计本身应该允许在处理客户端成为性能问题的情况下轻松进行多任务处理。

【讨论】:

    猜你喜欢
    • 2012-05-28
    • 2015-07-18
    • 1970-01-01
    • 2017-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多