图表:最快的方式把数据从接收UDP数据包

本文关键字:UDP 数据包 数据 方式 图表 | 更新日期: 2023-09-27 18:07:26

我试图写一个简单的UDP-to-Chart应用程序(windows窗体),将从以太网获得原始数据,并把它在一个特定的方式到图表。以下是我到目前为止所拥有的:带有两个图表的形式,一个接收UDP数据包的线程:

public void serverThread()
    {
        UdpClient udpClient = new UdpClient(Convert.ToInt16(tbEthPort.Text));
        while (_serverWork)
        {
            try
            {
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, Convert.ToInt16(tbEthPort.Text));
                Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
                if (receiveBytes.Length > 0)
                    for (i = 0; i < receiveBytes.Length; i++)
                    {
                        bigDataIn[i] = receiveBytes[i];
                    }

并绘制第二张图(以特定方式显示所有数据包内容):

if (graphicOn)
{
    for (i = 0; i < 32; i++)
    {
        graphicData[i + (graphicStep * 32)] = bigDataIn[i * 32 + graphicChan];
    }
    graphicStep++;
    if (graphicStep == 32) graphicStep = 0; 
    try
    {
        Invoke(new Action(() => { chartGraphic.Series["DataGraphic"].Points.DataBindXY(graphicEnum, graphicData);
        }));
    }
    catch
    {
    }
}

和一个带有计时器的主线程来绘制第一张图表。

private void tmrHisto_Tick(object sender, EventArgs e)
    {
        int[] slice = new int[32];
        for (int i = 0; i < 32; i++)
            slice[i] = bigDataIn[i + 32 * histogramArrayStep];
        histogramArrayStep++;
        if (histogramArrayStep == 32) histogramArrayStep = 0;
        chartHistogram.Series["DataHistogram"].Points.Clear();
        for (int i = 0; i < HISTO_XMAX; i++)
        {
            chartHistogram.Series["DataHistogram"].Points.AddXY(i, slice[i]);
        }
    }

在我的PC和其他几台电脑上,一切都很好,但是当我在旧PC上启动我的应用程序时,我的应用程序开始丢失数据包。当我开始执行Invoke chartGraphic时,开始丢包。我可以在WireShark中看到所有的数据包(大约每秒20个),没有任何丢失。我遇到了同样的问题,当定时器间隔设置为50ms而不是150ms时,但我不能再增加间隔了。

所以这就是我的问题-我可以提高图形绘制的速度,并停止低端PC的数据包丢失。或者我如何在调试期间模拟低端PC ?

图表:最快的方式把数据从接收UDP数据包

Invoke正在阻塞,因此您的receivethread将等待,直到绘制(DataBindXY)完成。尝试将其移出接收线程。

一个循环缓冲区看起来很快,但它看起来只保存引用。这并没有改善多少。此外,您从udpClient获得新的缓冲区:Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);循环缓冲区只有在"旧"内存被重用时才有用。

尝试并发队列将数据从接收线程传递到gui-thread/timer。确保重绑定/绘制在并发队列的锁之外。现在您获得了接收数据报的性能。


更新:

一些代码:

:

private List<byte[]> datagrams = new List<byte[]>();
public void serverThread()
{
    UdpClient udpClient = new UdpClient(Convert.ToInt16(tbEthPort.Text));
    while (_serverWork)
    {
        try
        {
            IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, Convert.ToInt16(tbEthPort.Text));
            Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
            // add to the queue
            lock (datagrams)
                datagrams.Add(receiveBytes);
        }
    }
}
GUI:

private Timer timer = new Timer();
public void timer_Tick(object sender, EventArgs e)
{
    byte[][] data;
    // lock the queue as short as possible. (create a copy with ToArray())
    // this way the receive thread can run again..
    // this is also know as bulk processing..
    lock (datagrams)
    {
        data = datagrams.ToArray();
        datagrams.Clear();
    }
    // if no packets received, don't update anything
    if(data.Length == 0)
        return;
    // process the received data (multiple datagrams)
    for(byte[] item in data)
    {
        ...
    }
    // Update chart
}

您可能会检查队列是否不会变得太大。当发生这种情况时,您的队列处理太慢了。您可以限制itemcount