I have the following method, which reads and deserializes packets from a NetworkStream asynchronously. Everything works, but CPU profiling shows that the very last line, in which I am awaiting an asynchronous read, is where the majority of my CPU usage comes from.
Have I implemented this badly/inefficiently, or is there something inherently wrong with the NetworkStream's async implementation?
public async Task<Packet> ReadAsync(CancellationToken cancellationToken)
{
while (true)
{
// Read through the available bytes until we find the start of a packet
while (start < length && buffer[start] != Packet.STX)
start++;
// Align the packet (and all successive bytes) with the beginning of the buffer
if (start > 0)
{
if (start < length)
Array.Copy(buffer, start, buffer, 0, length - start);
length -= start;
start = 0;
}
// Read through the available bytes until we find the end of the packet
while (end < length && buffer[end] != Packet.ETX)
end++;
// If we have a whole packet in the buffer, deserialize and return it
if (end < length)
{
byte[] data = new byte[end + 1];
Array.Copy(buffer, data, end + 1);
byte[] decoded = null;
Packet packet = null;
try
{
decoded = Packet.Decode(data);
}
catch (Exception ex)
{
throw new IOException("Could not decode packet", ex);
}
if (decoded != null)
{
try
{
packet = Packet.Deserialize(decoded);
}
catch (Exception ex)
{
throw new IOException("Could not deserialize packet", ex);
}
}
Array.Copy(buffer, end + 1, buffer, 0, length - (end + 1));
length -= end + 1;
end = 0;
if (packet != null)
return packet;
}
// If we read all available bytes while looking for the end of a packet
if (end == length)
{
if (length == buffer.Length)
throw new InsufficientMemoryException();
length += await Stream.ReadAsync(buffer, length, buffer.Length - length, cancellationToken);
}
}
}
I have updated the code to sleep between each call to ReadAsync, for roughly the amount of time the last read took:
var stopwatch = new Stopwatch();
var iteration = 0;
while (true)
{
// ...
var delay = stopwatch.Elapsed;
stopwatch.Restart();
if (iteration % 10 != 0)
await Task.Delay(delay);
length += await Stream.ReadAsync(buffer, length, buffer.Length - length, cancellationToken);
stopwatch.Stop();
iteration += 1;
}
This has drastically dropped the CPU usage. This is definitely a work-around, as it does not address the issue, but it works. I would love to hear anyone else's answers or opinions on this issue.
Related
I need to simulate a stream using a file...
This is what I was able to produce so far... the code works, but the streaming does not occur.
I have no experience with streams, so expect to find lot of bugs.
Stream chunkedStream = new MemoryStream();
var task = session.AnalyzeAsync(chunkedStream);
FillStream(chunkedStream);
while (!task.Wait(500))
{
Console.Write("\r" + ((float)chunkedStream.Position / 16000) + " Seconds");
if (!chunkedStream.CanWrite)
{
break;
}
}
private static async Task FillStream(Stream chunkedStream)
{
var stream = Properties.Resources.ResourceManager.GetStream("Sample");
byte[] chunk = new byte[16000];
while (true)
{
Thread.Sleep(1000);
int index = 0;
// There are various different ways of structuring this bit of code.
// Fundamentally we're trying to keep reading in to our chunk until
// either we reach the end of the stream, or we've read everything we need.
while (index < chunk.Length)
{
int bytesRead = stream.Read(chunk, index, chunk.Length - index);
if (bytesRead == 0)
{
break;
}
index += bytesRead;
}
if (index != 0) // Our previous chunk may have been the last one
{
// SendChunk(chunk, index); // index is the number of bytes in the chunk
chunkedStream.Write(chunk, 0, chunk.Length);
}
if (index != chunk.Length) // We didn't read a full chunk: we're done
{
chunkedStream.Close();
return;
}
}
}
public async Task AnalyzeAsync(Stream voiceStream)
{
IntervalHandler();
_StreamTask = UpStreamVoiceData(voiceStream);
await _StreamTask;
}
private async Task<Result<AnalysisResult>> UpStreamVoiceData(Stream voiceStream)
{
HttpWebRequest webRequest = WebRequestExtensions.CreateJsonPostRequest(_actions.upStream);
webRequest.ReadWriteTimeout = 1000000;
webRequest.Timeout = 10000000;
webRequest.SendChunked = true;
webRequest.AllowWriteStreamBuffering = false;
webRequest.AllowReadStreamBuffering = false;
using (var requeststream = webRequest.GetRequestStream())
{
await voiceStream.CopyStreamWithAutoFlush(requeststream);
requeststream.Close();
}
return webRequest.ReadJsonResponseAs<Result<AnalysisResult>>();
}
I am working on a new machine at my works which is controlled via a PC. Current stuck on talking to a camera system that is connected via Ethernet.
Here is my code of opening a connection.
TcpClient client;
NetworkStream stream;
public bool OpenConnection()
{
client = new TcpClient();
try
{
//Camera.Open();
client.Connect("192.168.0.10", 8500);
stream = client.GetStream();
}
catch (Exception ex)
{
return false;
}
return true;
}
So far so good. Once the connection is open I then request some information from the camera
The message format I am using has STX(0x02) to indicate the start of the message and ETX(0x03) as the end of the message.
char StartOfPacket = (char)0x02;
char EndOfPacket= (char)0x03;
public bool RetrieveDigits(out string Digits)
{
// Send the trigger cammand to the vision system
Digits = "";
bool EverythingOK = true;
string DataToSend = StartOfPacket + "T1" + EndOfPacket;
byte[] buff = Encoding.ASCII.GetBytes(DataToSend);
if (LogCameraEvent != null)
LogCameraEvent(">> " + DataToSend);
try
{
stream.Write(buff, 0, buff.Length);
}
catch (Exception ex)
{
Logging.Instance.LogExceptionToFile(MethodBase.GetCurrentMethod(), ex);
EverythingOK = false;
}
Thread.Sleep(100);
byte[] buffer;
if (EverythingOK)
{
// Check the response
buffer = ReadCamera(10);
// Process the packets
string[] packets = ProcessPackets(buffer);
if (packets != null)
{
if (packets.Length > 0)
{
bool TriggerFound = false;
for (int i = 0; i < packets.Length; i++)
{
if (packets[i] == "T1")
{
TriggerFound = true;
continue;
}
else if (TriggerFound)
{
// If we are here then we should now be the data that was requested
if (string.IsNullOrEmpty(packets[i]))
{
Digits = packets[i-1]; // previous packet may have data from previous trigger. Need to look into why this happens.
}
else
Digits = packets[i];
EverythingOK = true;
break;
}
else
EverythingOK = false;
}
}
else
{
Console.WriteLine("No Packets Recieved");
EverythingOK = false;
}
}
else
{
Console.WriteLine("No Packets Recieved");
EverythingOK = false;
}
}
return EverythingOK;
}
Here is the part where I think the issue may lie, getting the response.
private byte[] ReadCamera(int ExpectedLength)
{
if(ExpectedLength < 1)
{
ExpectedLength = 100;
}
byte[] Buffer = new byte[ExpectedLength];
int read = 0;
int chunk;
while(stream.DataAvailable)
{
chunk = stream.Read(Buffer, read, Buffer.Length-read);
read += chunk;
// If we have reached the end of our buffer, check to see if theres any more information
if(read == Buffer.Length)
{
if(!stream.DataAvailable)
return Buffer;
// Nope. Resize the buffer, put the byte we've just read and continue
byte[] newBuffer = new byte[Buffer.Length * 2];
Array.Copy(Buffer, newBuffer, Buffer.Length);
Buffer = newBuffer;
//read++;
}
else if(!stream.DataAvailable)
return Buffer;
Thread.Sleep(50);
}
// Buffer is now too big, shrink it
byte[] ret = new byte[read];
Array.Copy(Buffer, ret, read);
return ret;
}
private string[] ProcessPackets(byte[] data)
{
// look for the stat char
List<string> Packets = new List<string>();
byte Start = 0x02;
bool StartFound = false;
byte End = 0x03;
StringBuilder sb = new StringBuilder();
for(int i =0; i<data.Length; i++)
{
if(StartFound)
{
// Check to see if its the end
if (data[i] == End)
{
Packets.Add(sb.ToString());
sb.Clear();
}
else
sb.Append(Encoding.ASCII.GetChars(data, i, 1));
}
// Find the start
if (!StartFound)
{
if (data[i] == Start)
{
StartFound = true;
}
}
}
return Packets.ToArray();
}
Let me explain what should happen and then what is happening. Through RetrieveDigits method I am sending "T1" to the camera. What the camera will do is respond with "T1" followed by 2 ascii characters, for now we will say AA. Using a diagnostic app on the camera system I can monitor the Ethernet and I see the following.
(>> means Received by Camera)
(<< means Sent from Camera)
>>[STX]T1[ETX]
<<[STX]T1[ETX][STX]AA[ETX]
So I see what the camera has sent. I confirmed the PC has has received the data using wire shark.
Now further down in the RetrieveDigits method you can see I process the packets received, loop through the packets until I find "T1" and then assume the next packet will be the data I am after and I set Digits to this value.
What I am finding is that sometimes when I run the app I see that Digits is set to "".
I am also finding that sometimes my data received will be "AA" then "T1" rather than "T1" then "AA". What I suspect is happening is that when its back to front, the "AA" is actually from the previous data sent from the camera and for some reason it was missed when reading from the stream.
Any idea why this could be happening as I am reading the data until Stream.Available is false.
Edit:
Modified the ReadCamera code to process the packets, reading 1 byte at a time.
private string[] ReadCamera(int ExpectedLength, int ExpectedPackets)
{
List<string> Packets = new List<string>();
bool StartFound = false;
StringBuilder sb = new StringBuilder();
if(ExpectedLength < 1)
{
ExpectedLength = 100;
}
byte[] Buffer = new byte[ExpectedLength];
int read = 0;
while (true)
{
read += stream.Read(Buffer, read, 1);
// Check to see if the byte read is the start of a packet
if (Buffer[read - 1] == StartOfPacket)
{
StartFound = true;
}
else if (StartFound)
{
// Check to see if the byte read is the end of a packet
if (Buffer[read - 1] == EndOfPacket)
{
Packets.Add(sb.ToString());
sb.Clear();
StartFound = false;
if (Packets.Count == ExpectedPackets)
break;
}
else
{
sb.Append(Encoding.ASCII.GetChars(Buffer, read - 1, 1));
}
}
}
// For Debuggin purposes
foreach(string s in Packets)
if (LogCameraEvent != null)
LogCameraEvent("<< " + s);
return Packets.ToArray();
}
and modified calling the method like so
// Check the response
string[] packets = ReadCamera(10,2);
// Process the packets
//string[] packets = ProcessPackets(buffer);
I'm currently in a little bit of a pickle. I have some code that reads data from a socket whenever data is available but currently it is in a while loop on a separate thread and chews through 50% of the CPU when the function returns because of no data available. What I would really like is a blocking function for Sockets that blocks until data is available, or at least a OnData event that could be listened on. I originally converted this code from AS3 (Flash) but their sockets class has the OnData event I need... just in the wrong language.
I currently have this code in the code that handles a client connecting:
ServerThread = new Thread(() =>
{
while (server.Connected && ServerContinue)
{
ReceiveFromServer(server, client);
}
Disconnect(server, client, false);
});
ServerThread.Start();
And this is the code in ReceiveFromServer:
bool isReady = false;
int messageLength = 0;
int dataAvailable = 0;
UInt16 packetSize = 0;
byte[] temp = new byte[2];
do
{
dataAvailable = server.Available;
if (isReady)
{
if (dataAvailable >= messageLength)
{
byte[] temp1 = new byte[2000];
int bytesRead = server.Receive(temp1, 0, messageLength, SocketFlags.None);
byte[] data = new byte[bytesRead + 2];
Buffer.BlockCopy(temp1, 0, data, 2, messageLength);
Helpers.ByteArray tempo = data;
tempo.writeByte(temp[1]);
tempo.writeByte(temp[0]);
if (!VersionCheckPass)
{
Send(tempo, client);
return;
}
ServerPacketHandler(tempo, client);
messageLength = 0;
isReady = false;
temp = new byte[2];
}
}
else if(dataAvailable > 2)
{
server.Receive(temp, 0, 2, SocketFlags.None);
temp = temp.Reverse().ToArray();
packetSize = BitConverter.ToUInt16(temp, 0);
if (packetSize > 0)
{
messageLength = packetSize;
isReady = true;
}
}
}
while (dataAvailable > 2 && dataAvailable >= messageLength && ServerContinue);
But the issue here is that when dataAvailable is 0 the function simply returns, and then RecevieFromServer is called again in the thread. This means that alot of the CPU is used by simply calling ReceiveFromServer and then returning again.
I currently have Thread.Sleep(10) after ReceiveFromServer in the ServerThread but this is inefficient. So my question is, Is there a way to block until data is available or is there an event that I can handle? Or does anyone else have any suggestions on how to do the same thing I am currently doing but it doesn't loop endlessly whilst there is no data available.
Found a really easy (and obvious) solution to block until data is available. Call Socket.Receive with a receive size of 0. The socket blocks until there is data to receive, then reads 0 bytes from the socket, and unblocks. Its really quite marvelous :) Heres how I implemented it:
ServerThread = new Thread(() =>
{
byte[] zero = new byte[0];
while (Server.Connected && ServerContinue)
{
server.Receive(zero, 0, SocketFlags.None);
ReceiveFromServer(server, client);
}
Disconnect(server, client, false);
});
Thanks for all the help.
Josh
There is not a lot of re-write needed. Your code looks like it's just receiving the message and then passing off to another routine to process it.
My reply to this thread pretty much covers what you're wanting to do:
C# Sockets and Multithreading
My socketReadCallBack function is:
private void OnDataReceived(IAsyncResult asyn)
{
ReceiveState rState = (ReceiveState)asyn.AsyncState;
Socket client = rState.Client;
SocketError socketError = SocketError.TypeNotFound;
if (!client.Connected)
{
// Not Connected anymore ?
return;
}
_LastComms = DateTime.Now;
_LastIn = _LastComms;
int dataOffset = 0;
int restOfData = 0;
int dataRead = 0;
Boolean StreamClosed = false;
long rStateDataLength = 0;
long LastrStateDataLength = 0;
try
{
dataRead = client.EndReceive(asyn, out socketError);
}
catch (Exception excpt)
{
// Handle error - use your own code..
}
if (socketError != SocketError.Success)
{
// Has Connection been lost ?
OnConnectionDropped(client);
return;
}
if (dataRead <= 0)
{
// Has connection been lost ?
OnConnectionDropped(client);
return;
}
while (dataRead > 0)
{
//check to determine what income data contain: size prefix or message
if (!rState.DataSizeReceived)
{
//there is already some data in the buffer
if (rState.Data.Length > 0)
{
restOfData = PrefixSize - (int)rState.Data.Length;
rState.Data.Write(rState.Buffer, dataOffset, restOfData);
dataRead -= restOfData;
dataOffset += restOfData;
}
else if (dataRead >= PrefixSize)
{ //store whole data size prefix
rState.Data.Write(rState.Buffer, dataOffset, PrefixSize);
dataRead -= PrefixSize;
dataOffset += PrefixSize;
}
else
{ // store only part of the size prefix
rState.Data.Write(rState.Buffer, dataOffset, dataRead);
dataOffset += dataRead;
dataRead = 0;
}
if (rState.Data.Length == PrefixSize)
{ //we received data size prefix
rState.DataSize = BitConverter.ToInt32(rState.Data.GetBuffer(), 0);
rState.DataSizeReceived = true;
//reset internal data stream
rState.Data.Position = 0;
rState.Data.SetLength(0);
}
else
{ //we received just part of the prefix information
//issue another read
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack),
rState);
return;
}
}
//at this point we know the size of the pending data
// Object disposed exception may raise here
try
{
rStateDataLength = rState.Data.Length;
LastrStateDataLength = rStateDataLength;
}
catch (ObjectDisposedException Ode)
{
StreamClosed = true;
}
if (!StreamClosed)
{
if ((rStateDataLength + dataRead) >= rState.DataSize)
{ //we have all the data for this message
restOfData = rState.DataSize - (int)rState.Data.Length;
rState.Data.Write(rState.Buffer, dataOffset, restOfData);
//Console.WriteLine("Data message received. Size: {0}",
// rState.DataSize);
// Is this a heartbeat message ?
if (rState.Data.Length == 2)
{
// Yes
HeartBeatReceived();
}
else
{
//charArray = new char[uniEncoding.GetCharCount(
//byteArray, 0, count)];
//uniEncoding.GetDecoder().GetChars(
// byteArray, 0, count, charArray, 0);
//Console.WriteLine(charArray);
//rState.Data.Position = 0;
DecodeMessageReceived(GetStringFromStream(rState.Data));
}
dataOffset += restOfData;
dataRead -= restOfData;
//message received - cleanup internal memory stream
rState.Data = new MemoryStream();
rState.Data.Position = 0;
rState.DataSizeReceived = false;
rState.DataSize = 0;
if (dataRead == 0)
{
//no more data remaining to process - issue another receive
if (_IsConnected)
{
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack),
rState);
return;
}
}
else
continue; //there's still some data to process in the buffers
}
else
{ //there is still data pending, store what we've
//received and issue another BeginReceive
if (_IsConnected)
{
rState.Data.Write(rState.Buffer, dataOffset, dataRead);
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack), rState);
dataRead = 0;
}
}
}
else
{
// Stream closed, but have we read everything ?
if (LastrStateDataLength + dataRead == rState.DataSize)
{
// We're equal, get ready for more
//no more data remaining to process - issue another receive
if (_IsConnected)
{
client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
SocketFlags.None, new AsyncCallback(socketReadCallBack),
rState);
}
return;
}
else
{
// We should have more..
// Report Error
}
}
// If we've been disconnected, provide a graceful exit
if (!_IsConnected)
dataRead = -1;
}
}
I've got a few more things in here than you need like provision for a heartbeat message and raising events on connection dropped etc.
I was searching for a BinaryReader.Skip function, while I came across this feature request on msdn.
He said you can provide your own BinaryReader.Skip() function, by using this.
Only looking at this code, I'm wondering why he chose this way to skip a certain amount of bytes:
for (int i = 0, i < count; i++) {
reader.ReadByte();
}
Is there a difference between that and:
reader.ReadBytes(count);
Even if it's just a small optimalisation, I'd like to undestand. Because now it doesnt make sense to me why you would use the for loop.
public void Skip(this BinaryReader reader, int count) {
if (reader.BaseStream.CanSeek) {
reader.BaseStream.Seek(count, SeekOffset.Current);
}
else {
for (int i = 0, i < count; i++) {
reader.ReadByte();
}
}
}
No, there is no difference. EDIT: Assuming that the stream has enough byes
The ReadByte method simply forwards to the underlying Stream's ReadByte method.
The ReadBytes method calls the underlying stream's Read until it reads the required number of bytes.
It's defined like this:
public virtual byte[] ReadBytes(int count) {
if (count < 0) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length <= Contract.OldValue(count));
Contract.EndContractBlock();
if (m_stream==null) __Error.FileNotOpen();
byte[] result = new byte[count];
int numRead = 0;
do {
int n = m_stream.Read(result, numRead, count);
if (n == 0)
break;
numRead += n;
count -= n;
} while (count > 0);
if (numRead != result.Length) {
// Trim array. This should happen on EOF & possibly net streams.
byte[] copy = new byte[numRead];
Buffer.InternalBlockCopy(result, 0, copy, 0, numRead);
result = copy;
}
return result;
}
For most streams, ReadBytes will probably be faster.
ReadByte will throw an EndOfStreamException if the end of the stream is reached, whereas ReadBytes will not. It depends on whether you want Skip to throw if it cannot skip the requested number of bytes without reaching the end of the stream.
ReadBytes is faster than multiple ReadByte calls.
Its a very small optimization which will occasionally skip bytes (rather then reading them into ReadByte) Think of it this way
if(vowel)
{
println(vowel);
}
else
{
nextLetter();
}
If you can prevent that extra function call you save a little runtime
On full .Net Framework I use the following code:
socket.SetSocketOption(
SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, readTimeout);
socket.SetSocketOption(
SocketOptionLevel.Socket, SocketOptionName.SendTimeout, writeTimeout);
However, Windows Mobile does not support this and throws exceptions.
I am currently in the middle of testing this solution for implementing timeouts.
Does anyone know a better way? I'd like to avoid spawning multiple threads if possible, this is an embedded device after all.
This code works, raising timeouts when expected (it is a modified version of the example I linked in the question):
// copied from Mono, because CF lacks this enum
enum SocketError
{
IOPending = 997,
NoBufferSpaceAvailable = 10055,
TimedOut = 10060,
WouldBlock = 10035
}
// milliseconds
int receiveTimeout = 20000;
int sendTimeout = 20000;
public override int Read(byte[] buffer, int offset, int size)
{
int startTickCount = Environment.TickCount;
int received = 0;
do
{
List<Socket> sock = new List<Socket>(new Socket[] {socket});
Socket.Select(sock, null, null, receiveTimeout*1000 + 1);
if (Environment.TickCount > startTickCount + receiveTimeout)
throw new SocketException((int) SocketError.TimedOut);
try
{
received += socket.Receive(buffer, offset + received,
size - received, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.ErrorCode == (int) SocketError.WouldBlock ||
ex.ErrorCode == (int) SocketError.IOPending ||
ex.ErrorCode == (int) SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably empty, wait and try again
Thread.Sleep(30);
}
else
throw; // any serious error occurr
}
} while (received < size);
return received;
}
public override void Write(byte[] buffer, int offset, int size)
{
int startTickCount = Environment.TickCount;
int sent = 0;
do
{
List<Socket> sock = new List<Socket>(new Socket[] {socket});
Socket.Select(null, sock, null, sendTimeout*1000 + 1);
if (Environment.TickCount > startTickCount + sendTimeout)
throw new SocketException((int) SocketError.TimedOut);
try
{
sent += socket.Send(buffer, offset + sent,
size - sent, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.ErrorCode == (int) SocketError.WouldBlock ||
ex.ErrorCode == (int) SocketError.IOPending ||
ex.ErrorCode == (int) SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
throw; // any serious error occurr
}
} while (sent < size);
}
The crucial element missing from the example I found is Socket.Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds). Bear in mind that this method may modify the list that is passed to it (that's why my code creates a new one each time) and measures time in microseconds instead of milliseconds. And remember to use Environment.TickCount (which is a monotonic time source) instead of DateTime.Now for measuring time.
I had the same issue. This can be fixed uding a EventWaitHandle which is described here post