Reputation: 109
I'm trying to write a simple UDP-to-Chart app (for windows forms) that will get raw data from ethernet, and put it in a specific way to a chart. Here's what i have so far: Form with two charts, a Thread to receive UDP packets:
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];
}
and draw second chart (that shows all packet contents in a specific way):
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
{
}
}
and a main thread with timer to draw first chart.
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]);
}
}
Everything works fine, on my PC, and several others, but when I launch my app on old PC's, my app starts to loose packets. Packet loss starts when I begin to Invoke
chartGraphic
. I can see all packets (about 20 in a second) in WireShark without any losses. I experienced same problems, when timer interval was set to 50ms instead of 150ms, but I can't increase interval anymore.
So that's my question - can I increase the speed of graphic drawing, and stop packet losses on low-end PC's. Or how can I emulate low-end PC during debug?
Upvotes: 4
Views: 890
Reputation: 22073
Invoke
is blocking, so your receivethread will wait until the drawing (DataBindXY) is complete. Try to move it outside the receive thread.
A circular buffer looks fast, but it looks that it only holds references. This doesn't improve much. Besides, you are getting new buffers from the udpClient: Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
Circular buffers are only usefull when 'old' memory is reused.
Try a concurrent queue to pass data from the receive thread to the gui-thread/timer. Be sure that rebinding/drawing is outside the lock of the concurrent queue. Now you're getting performance to receive datagrams.
UPDATE:
Some pseudo code:
Back:
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
}
You might check if the queue doesn't grow too large. When this happens, your queue processing is too slow. You could limit the itemcount.
Upvotes: 2