I am communicating with a device via UDP, I send a message and I receive the response.
public byte[] Rx_message = new byte[12];
public byte[] packedMessage2 = new byte[12];
public IPEndPoint sendEndPoint;
public void senderUdpClient(byte Type, byte CommandC, byte CodeCmd, Int32 X, Int32 Y)
{
string serverIP = "192.168.2.11";
int sendPort = 40960;
int receivePort = 40961;
var span = new Span<byte>(packedMessage2);
span[0] = Type;
span[1] = CommandC;
span[2] = CodeCmd;
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(3, 4), X);
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(7, 4), Y);
var sum = unchecked((byte)packedMessage2.Take(11).Sum(x => x));
span[11] = sum;
sendEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), sendPort);
try
{
UdpClient senderClient = new UdpClient();
senderClient.Connect(this.sendEndPoint);
senderClient.Send(packedMessage2, packedMessage2.Length);
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
UdpClient receivingUdpClient = new UdpClient(receivePort);
receivingUdpClient.Client.ReceiveTimeout = 50;
// receive message
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
senderClient.Close();
receivingUdpClient.Close();
}
catch (Exception ex)
{
}
}
I want to add a retry mechanism if the equipment does not respond in the event of a network problem,
(if I have an error: Rx_message [0] = -1 and if all goes well Rx_message [0] = 2)
Normally I should do 2 retry if I can connect I continue, otherwise I display an error message
public byte[] Rx_message = new byte[12];
public byte[] packedMessage2 = new byte[12];
public IPEndPoint sendEndPoint;
public int Connected = 0;
public void senderUdpClient(byte Type, byte CommandC, byte CodeCmd, Int32 X, Int32 Y)
{
string serverIP = "192.168.2.11";
int sendPort = 40960;
int receivePort = 40961;
var span = new Span<byte>(packedMessage2);
span[0] = Type;
span[1] = CommandC;
span[2] = CodeCmd;
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(3, 4), X);
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(7, 4), Y);
var sum = unchecked((byte)packedMessage2.Take(11).Sum(x => x));
span[11] = sum;
sendEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), sendPort);
try
{
for(int i = 0; i< 2; i++)
{
UdpClient senderClient = new UdpClient();
senderClient.Connect(this.sendEndPoint);
senderClient.Send(packedMessage2, packedMessage2.Length);
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
UdpClient receivingUdpClient = new UdpClient(receivePort);
receivingUdpClient.Client.ReceiveTimeout = 50;
// receive message
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
//first case where it is connected and I receive my answer correctly
if (Rx_message[0] == 2)
{
Connected = 1;
break;
}
//if I can't connect
if (Connected == 0)
{
//first try
if (i < 1)
{
Thread.Sleep(20);
continue;
}
//second try
if (i == 1)
{
//these two lines I put it just so that the code goes to try and shows me the error code
receivingUdpClient.Client.ReceiveTimeout = 1500;
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
}
}
senderClient.Close();
receivingUdpClient.Close();
}
}
catch (Exception ex)
{
if (Connected == 0)
{
MessageBox.Show("Error Connection");
}
sbyte type_message = Convert.ToSByte(Rx_message[0]);
if (type_message == -1)
{
if (Rx_message[3] == 1)
{
MessageBox.Show("Type invalide");
}
if (Rx_message[3] == 2)
{
MessageBox.Show("Commande invalide");
}
if (Rx_message[3] == 3)
{
MessageBox.Show("Argument invalide!");
}
if (Rx_message[3] == 4)
{
MessageBox.Show("Erreur CS!");
}
}
}
}
Here is the code I tried to do, once it gets to the reading line
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
he goes directly to Catch I don't know how I can force him to continue the code, otherwise someone can help me improve this mechanism
I have a server side app written in C
struct recv_packet
{
int magic;
int code;
int length;
char *body;
};
char send_buff[1024+1] = "";
ZeroMemory(&send_buff, 1024);
memset(send_buff, 'A', 1024);
//send_buff[1024] = '\0';
recv_packet rcv_pkt = { 0 };
rcv_pkt.magic = MAGIC;
rcv_pkt.code = 0;
rcv_pkt.length = strlen(send_buff);
rcv_pkt.body = send_buff;
int size = sizeof(rcv_pkt.magic) + sizeof(rcv_pkt.code) + sizeof(rcv_pkt.length) + 1024+1;
if (send(ClientSocket, (char *)&rcv_pkt, size, 0) == SOCKET_ERROR)
{
printf("Error %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
On the other side i grab this packet like this:
public struct recv_packet
{
public int magic;
public int code;
public int length;
public byte[] body;
};
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int port = 4000;
TcpClient client = new TcpClient("127.0.0.1", 4000);
NetworkStream nws = client.GetStream();
BinaryWriter bw = new BinaryWriter(nws);
BinaryReader br = new BinaryReader(nws);
byte[] buff = new byte[512];
send_packet pkt = new send_packet();
pkt.magic = magic;
pkt.cmd = (int)command.MOVE_MOUSE;
while (true)
{
bw.Write(pkt.magic);
bw.Write(pkt.cmd);
//br.Read(buff, 0, 512);
recv_packet rcv_pkt = new recv_packet();
rcv_pkt.magic = br.ReadInt32();
rcv_pkt.code = br.ReadInt32();
rcv_pkt.length = br.ReadInt32();
rcv_pkt.body = br.ReadBytes(rcv_pkt.length);
//string str = rcv_pkt.length.ToString();
string str = System.Text.Encoding.Default.GetString(rcv_pkt.body);
MessageBox.Show(str);
}
}
So it suppose that body will have only '65', but instead I've got some trash in it.
Why could this happen? Thank you for your time.
As I understood there are few ways of resolving this problem - one of them is to redecrlare struct a little bit and then creare a buffer, where all structure elements will be fitted one-by-one. So the solution looks like this:
char send_buff[1024+1] = "";
ZeroMemory(&send_buff, 1025);
memset(send_buff, 'A', 1024);
recv_packet *rcv_pkt = (recv_packet *)malloc(sizeof(recv_packet)+1024+1);
//recv_packet rcv_pkt = { 0 };
rcv_pkt->magic = MAGIC;
rcv_pkt->code = 0;
rcv_pkt->length = strlen(send_buff);
memcpy(rcv_pkt->body, send_buff, 1025);
int size = sizeof(rcv_pkt->magic) + sizeof(rcv_pkt->code) + sizeof(rcv_pkt->length) + 1024 + 1;
//printf("%d", size);
//getchar();
//return 0;
//if (send(ClientSocket, rcv_pkt.body, rcv_pkt.length, 0) == SOCKET_ERROR)
if (send(ClientSocket, (char *)rcv_pkt, size, 0) == SOCKET_ERROR)
I would like to do something roughly equivalent to the code example below. I want to generate and serve a stream of data without necessarily having the entire data set in memory at any one time.
It seems like I would need some implementation of Stream that accepts an IEnumerable<string> (or IEnumerable<byte>) in its constructor. Internally this Stream would only walk the IEnumerable as the Stream is being read or as needed. But I don't know of any Stream implementation like this.
Am I on the right track? Do you know of any way to do something like this?
public FileStreamResult GetResult()
{
IEnumerable<string> data = GetDataForStream();
Stream dataStream = ToStringStream(Encoding.UTF8, data);
return File(dataStream, "text/plain", "Result");
}
private IEnumerable<string> GetDataForStream()
{
StringBuilder sb;
for (int i = 0; i < 10000; i++)
{
yield return i.ToString();
yield return "\r\n";
}
}
private Stream ToStringStream(Encoding encoding, IEnumerable<string> data)
{
// I have to write my own implementation of stream?
throw new NotImplementedException();
}
Here's a read-only Stream implementation that uses an IEnumerable<byte> as input:
public class ByteStream : Stream, IDisposable
{
private readonly IEnumerator<byte> _input;
private bool _disposed;
public ByteStream(IEnumerable<byte> input)
{
_input = input.GetEnumerator();
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => 0;
public override long Position { get; set; } = 0;
public override int Read(byte[] buffer, int offset, int count)
{
int i = 0;
for (; i < count && _input.MoveNext(); i++)
buffer[i + offset] = _input.Current;
return i;
}
public override long Seek(long offset, SeekOrigin origin) => throw new InvalidOperationException();
public override void SetLength(long value) => throw new InvalidOperationException();
public override void Write(byte[] buffer, int offset, int count) => throw new InvalidOperationException();
public override void Flush() => throw new InvalidOperationException();
void IDisposable.Dispose()
{
if (_disposed)
return;
_input.Dispose();
_disposed= true;
}
}
What you then still need is a function that converts IEnumerable<string> to IEnumerable<byte>:
public static IEnumerable<byte> Encode(IEnumerable<string> input, Encoding encoding)
{
byte[] newLine = encoding.GetBytes(Environment.NewLine);
foreach (string line in input)
{
byte[] bytes = encoding.GetBytes(line);
foreach (byte b in bytes)
yield return b;
foreach (byte b in newLine)
yield return b;
}
}
And finally, here's how to use this in your controller:
public FileResult GetResult()
{
IEnumerable<string> data = GetDataForStream();
var stream = new ByteStream(Encode(data, Encoding.UTF8));
return File(stream, "text/plain", "Result.txt");
}
I created a class called ProducerConsumerStream that does this. The producer writes data to the stream and the consumer reads. There's a buffer in the middle so that the producer can "write ahead" a little bit. You can define the size of the buffer.
Anyway, if it's not exactly what you're looking for, I suspect it will give you a good idea of how it's done. See Building a new type of stream.
Update
The link went stale, so I've copied my code here. The original article is still available on the Wayback machine at https://web.archive.org/web/20151210235510/http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=852
First, the ProducerConsumerStream class:
using System;
using System.IO;
using System.Threading;
using System.Diagnostics;
namespace Mischel.IO
{
// This class is safe for 1 producer and 1 consumer.
public class ProducerConsumerStream : Stream
{
private byte[] CircleBuff;
private int Head;
private int Tail;
public bool IsAddingCompleted { get; private set; }
public bool IsCompleted { get; private set; }
// For debugging
private long TotalBytesRead = 0;
private long TotalBytesWritten = 0;
public ProducerConsumerStream(int size)
{
CircleBuff = new byte[size];
Head = 1;
Tail = 0;
}
[Conditional("JIM_DEBUG")]
private void DebugOut(string msg)
{
Console.WriteLine(msg);
}
[Conditional("JIM_DEBUG")]
private void DebugOut(string fmt, params object[] parms)
{
DebugOut(string.Format(fmt, parms));
}
private int ReadBytesAvailable
{
get
{
if (Head > Tail)
return Head - Tail - 1;
else
return CircleBuff.Length - Tail + Head - 1;
}
}
private int WriteBytesAvailable { get { return CircleBuff.Length - ReadBytesAvailable - 1; } }
private void IncrementTail()
{
Tail = (Tail + 1) % CircleBuff.Length;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (disposed)
{
throw new ObjectDisposedException("The stream has been disposed.");
}
if (IsCompleted)
{
throw new EndOfStreamException("The stream is empty and has been marked complete for adding.");
}
if (count == 0)
{
return 0;
}
lock (CircleBuff)
{
DebugOut("Read: requested {0:N0} bytes. Available = {1:N0}.", count, ReadBytesAvailable);
while (ReadBytesAvailable == 0)
{
if (IsAddingCompleted)
{
IsCompleted = true;
return 0;
}
Monitor.Wait(CircleBuff);
}
// If Head < Tail, then there are bytes available at the end of the buffer
// and also at the front of the buffer.
// If reading from Tail to the end doesn't fulfill the request,
// and there are still bytes available,
// then read from the start of the buffer.
DebugOut("Read: Head={0}, Tail={1}, Avail={2}", Head, Tail, ReadBytesAvailable);
IncrementTail();
int bytesToRead;
if (Tail > Head)
{
// When Tail > Head, we know that there are at least
// (CircleBuff.Length - Tail) bytes available in the buffer.
bytesToRead = CircleBuff.Length - Tail;
}
else
{
bytesToRead = Head - Tail;
}
// Don't read more than count bytes!
bytesToRead = Math.Min(bytesToRead, count);
Buffer.BlockCopy(CircleBuff, Tail, buffer, offset, bytesToRead);
Tail += (bytesToRead - 1);
int bytesRead = bytesToRead;
// At this point, either we've exhausted the buffer,
// or Tail is at the end of the buffer and has to wrap around.
if (bytesRead < count && ReadBytesAvailable > 0)
{
// We haven't fulfilled the read.
IncrementTail();
// Tail is always equal to 0 here.
bytesToRead = Math.Min((count - bytesRead), (Head - Tail));
Buffer.BlockCopy(CircleBuff, Tail, buffer, offset + bytesRead, bytesToRead);
bytesRead += bytesToRead;
Tail += (bytesToRead - 1);
}
TotalBytesRead += bytesRead;
DebugOut("Read: returning {0:N0} bytes. TotalRead={1:N0}", bytesRead, TotalBytesRead);
DebugOut("Read: Head={0}, Tail={1}, Avail={2}", Head, Tail, ReadBytesAvailable);
Monitor.Pulse(CircleBuff);
return bytesRead;
}
}
public override void Write(byte[] buffer, int offset, int count)
{
if (disposed)
{
throw new ObjectDisposedException("The stream has been disposed.");
}
if (IsAddingCompleted)
{
throw new InvalidOperationException("The stream has been marked as complete for adding.");
}
lock (CircleBuff)
{
DebugOut("Write: requested {0:N0} bytes. Available = {1:N0}", count, WriteBytesAvailable);
int bytesWritten = 0;
while (bytesWritten < count)
{
while (WriteBytesAvailable == 0)
{
Monitor.Wait(CircleBuff);
}
DebugOut("Write: Head={0}, Tail={1}, Avail={2}", Head, Tail, WriteBytesAvailable);
int bytesToCopy = Math.Min((count - bytesWritten), WriteBytesAvailable);
CopyBytes(buffer, offset + bytesWritten, bytesToCopy);
TotalBytesWritten += bytesToCopy;
DebugOut("Write: {0} bytes written. TotalWritten={1:N0}", bytesToCopy, TotalBytesWritten);
DebugOut("Write: Head={0}, Tail={1}, Avail={2}", Head, Tail, WriteBytesAvailable);
bytesWritten += bytesToCopy;
Monitor.Pulse(CircleBuff);
}
}
}
private void CopyBytes(byte[] buffer, int srcOffset, int count)
{
// Insert at head
// The copy might require two separate operations.
// copy as much as can fit between Head and end of the circular buffer
int offset = srcOffset;
int bytesCopied = 0;
int bytesToCopy = Math.Min(CircleBuff.Length - Head, count);
if (bytesToCopy > 0)
{
Buffer.BlockCopy(buffer, offset, CircleBuff, Head, bytesToCopy);
bytesCopied = bytesToCopy;
Head = (Head + bytesToCopy) % CircleBuff.Length;
offset += bytesCopied;
}
// Copy the remainder, which will go from the beginning of the buffer.
if (bytesCopied < count)
{
bytesToCopy = count - bytesCopied;
Buffer.BlockCopy(buffer, offset, CircleBuff, Head, bytesToCopy);
Head = (Head + bytesToCopy) % CircleBuff.Length;
}
}
public void CompleteAdding()
{
if (disposed)
{
throw new ObjectDisposedException("The stream has been disposed.");
}
lock (CircleBuff)
{
DebugOut("CompleteAdding: {0:N0} bytes written.", TotalBytesWritten);
IsAddingCompleted = true;
Monitor.Pulse(CircleBuff);
}
}
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return true; } }
public override void Flush() { /* does nothing */ }
public override long Length { get { throw new NotImplementedException(); } }
public override long Position
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
private bool disposed = false;
protected override void Dispose(bool disposing)
{
if (!disposed)
{
base.Dispose(disposing);
disposed = true;
}
}
}
}
And an example of how to use it:
class Program
{
static readonly string TestText = "This is a test of the emergency broadcast system.";
static readonly byte[] TextBytes = Encoding.UTF8.GetBytes(TestText);
const int Megabyte = 1024 * 1024;
const int TestBufferSize = 12;
const int ProducerBufferSize = 4;
const int ConsumerBufferSize = 5;
static void Main(string[] args)
{
Console.WriteLine("TextBytes contains {0:N0} bytes.", TextBytes.Length);
using (var pcStream = new ProducerConsumerStream(TestBufferSize))
{
Thread ProducerThread = new Thread(ProducerThreadProc);
Thread ConsumerThread = new Thread(ConsumerThreadProc);
ProducerThread.Start(pcStream);
Thread.Sleep(2000);
ConsumerThread.Start(pcStream);
ProducerThread.Join();
ConsumerThread.Join();
}
Console.Write("Done. Press Enter.");
Console.ReadLine();
}
static void ProducerThreadProc(object state)
{
Console.WriteLine("Producer: started.");
var pcStream = (ProducerConsumerStream)state;
int offset = 0;
while (offset < TestText.Length)
{
int bytesToWrite = Math.Min(ProducerBufferSize, TestText.Length - offset);
pcStream.Write(TextBytes, offset, bytesToWrite);
offset += bytesToWrite;
}
pcStream.CompleteAdding();
Console.WriteLine("Producer: {0:N0} total bytes written.", offset);
Console.WriteLine("Producer: exit.");
}
static void ConsumerThreadProc(object state)
{
Console.WriteLine("Consumer: started.");
var instream = (ProducerConsumerStream)state;
int testOffset = 0;
var inputBuffer = new byte[TextBytes.Length];
int bytesRead;
do
{
int bytesToRead = Math.Min(ConsumerBufferSize, inputBuffer.Length - testOffset);
bytesRead = instream.Read(inputBuffer, testOffset, bytesToRead);
//Console.WriteLine("Consumer: {0:N0} bytes read.", bytesRead);
testOffset += bytesRead;
} while (bytesRead != 0);
Console.WriteLine("Consumer: {0:N0} total bytes read.", testOffset);
// Compare bytes read with TextBytes
for (int i = 0; i < TextBytes.Length; ++i)
{
if (inputBuffer[i] != TextBytes[i])
{
Console.WriteLine("Read error at position {0}", i);
break;
}
}
Console.WriteLine("Consumer: exit.");
}
}
I had the same problem. In my case a third party package only accepts streams but I have an IEnumerable, and couldn't find an answer online so I wrote my own, which I'll share:
public class IEnumerableStringReader : TextReader
{
private readonly IEnumerator<string> _enumerator;
private bool eof = false; // is set to true when .MoveNext tells us there is no more data.
private char[] curLine = null;
private int curLinePos = 0;
private bool disposed = false;
public IEnumerableStringReader(IEnumerable<string> input)
{
_enumerator = input.GetEnumerator();
}
private void GetNextLine()
{
if (eof) return;
eof = !_enumerator.MoveNext();
if (eof) return;
curLine = $"{_enumerator.Current}\r\n" // IEnumerable<string> input implies newlines exist betweent he lines.
.ToCharArray();
curLinePos = 0;
}
public override int Peek()
{
if (disposed) throw new ObjectDisposedException("The stream has been disposed.");
if (curLine == null || curLinePos == curLine.Length) GetNextLine();
if (eof) return -1;
return curLine[curLinePos];
}
public override int Read()
{
if (disposed) throw new ObjectDisposedException("The stream has been disposed.");
if (curLine == null || curLinePos == curLine.Length) GetNextLine();
if (eof) return -1;
return curLine[curLinePos++];
}
public override int Read(char[] buffer, int index, int count)
{
if (disposed) throw new ObjectDisposedException("The stream has been disposed.");
if (count == 0) return 0;
int charsReturned = 0;
int maxChars = Math.Min(count, buffer.Length - index); // Assuming we dont run out of input chars, we return count characters if we can. If the space left in the buffer is not big enough we return as many as will fit in the buffer.
while (charsReturned < maxChars)
{
if (curLine == null || curLinePos == curLine.Length) GetNextLine();
if (eof) return charsReturned;
int maxCurrentCopy = maxChars - charsReturned;
int charsAtTheReady = curLine.Length - curLinePos; // chars available in current line
int copySize = Math.Min(maxCurrentCopy, charsAtTheReady); // stop at end of buffer.
// cant use Buffer.BlockCopy because it's byte based and we're dealing with chars.
Array.ConstrainedCopy(curLine, curLinePos, buffer, index, copySize);
index += copySize;
curLinePos += copySize;
charsReturned += copySize;
}
return charsReturned;
}
public override string ReadLine()
{
if (curLine == null || curLinePos == curLine.Length) GetNextLine();
if (eof) return null;
if (curLinePos > 0) // this is necessary in case the client uses both Read() and ReadLine() calls
{
var tmp = new string(curLine, curLinePos, (curLine.Length - curLinePos) - 2); // create a new string from the remainder of the char array. The -2 is because GetNextLine appends a crlf.
curLinePos = curLine.Length; // so next call will re-read
return tmp;
}
// read full line.
curLinePos = curLine.Length; // so next call will re-read
return _enumerator.Current; // if all the client does is call ReadLine this (faster) code path will be taken.
}
protected override void Dispose(bool disposing)
{
if (!disposed)
{
_enumerator.Dispose();
base.Dispose(disposing);
disposed = true;
}
}
}
In my case, I want to use it as input to Datastreams.Csv:
using (var tr = new IEnumerableStringReader(input))
using (var reader = new CsvReader(tr))
{
while (reader.ReadRecord())
{
// do whatever
}
}
Using the EnumerableToStream Nuget package, you would implement your method like so:
using EnumerableToStream;
private Stream ToStringStream(Encoding encoding, IEnumerable<string> data)
{
return data.ToStream(encoding);
}
I had the same requirement and ended up rolling my own implementation which I have been using for a while now. Getting all the nitty-gritty details just right took some time and effort. For instance, you want your IEnumerable to be disposed after the stream is read to the end and you don't want multibyte characters to be partially written to the buffer.
In this particular implementation, reading the stream does zero allocations, unlike other implementations using encoding.GetBytes(line).
After seeing this question, I decided to release the code as a Nuget package. Hope it saves you a few hours. The source code is on GitHub.
Steve Sadler wrote a perfectly working answer. However, he makes it way more difficult than needed
According to the reference source of TextReader you'll need only override Peek and Read:
A subclass must minimally implement the Peek() and Read() methods.
So first I write a function that converts IEnumerable<string> into IEnumerable<char> where a new line is added at the end of each string:
private static IEnumerable<char> ReadCharacters(IEnumerable<string> lines)
{
foreach (string line in lines)
{
foreach (char c in line + Environment.NewLine)
{
yield return c;
}
}
}
Environment.NewLine is the part that adds the new line at the end of each string.
Now the class is failry straightforward:
class EnumStringReader : TextReader
{
public EnumStringReader(IEnumerable<string> lines)
{
this.enumerator = ReadCharacters(lines).GetEnumerator();
this.dataAvailable = this.enumerator.MoveNext();
}
private bool disposed = false;
private bool dataAvailable;
private readonly IEnumerator<char> enumerator;
The constructor takes a sequence of lines to read. It uses this sequence and the earlier written function to convert the sequence into a sequence of characters with the added Environment.NewLine.
It gets the enumerator of the converted sequence, and moves to the first character. It remembers whether there is a first character in DataAvailable
Now we are ready to Peek: if no data available: return -1, otherwise return the current character as int. Do not move forward:
public override int Peek()
{
this.ThrowIfDisposed();
return this.dataAvailable ? this.enumerator.Current : -1;
}
Read: if no data available, return -1, otherwise return the current character as int. Move forward to the next character and remember whether there is data available:
public override int Read()
{
this.ThrowIfDisposed();
if (this.dataAvailable)
{
char nextChar = this.enumerator.Current;
this.dataAvailable = this.enumerator.MoveNext();
return (int)nextChar;
}
else
{
return -1;
}
}
Don't forget to override Dispose(bool) where you dispose the enumerator.
That is all that is needed. All other functions will use these two.
Now to fill your stream with the lines:
IEnumerable<string> lines = ...
using (TextWriter writer = System.IO.File.CreateText(...))
{
using (TextReader reader = new EnumStringReader(lines);
{
// either write per char:
while (reader.Peek() != -1)
{
char c = (char)reader.Read();
writer.Write(c);
}
// or write per line:
string line = reader.ReadLine();
// line is without newLine!
while (line != null)
{
writer.WriteLine(line);
line = reader.ReadLine();
}
// or write per block
buffer buf = new char[4096];
int nrRead = reader.ReadBlock(buf, 0, buf.Length)
while (nrRead > 0)
{
writer.Write(buf, 0, nrRead);
nrRead = reader.ReadBlock(buf, 0, buf.Length);
}
}
}
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 am trying to make a demonstration for web socket communication for one of our new projects at work, and I've been trying to revise an old web socket server program I wrote quite a while ago.
It does a proper handshake and can send data to the client properly (which is all it REALLY needs to do), but the client data that comes back is in the special web socket communication protocol, and I am not that good with working with binary or hex or encryption algorithms.
I know from research that the text I'm receiving back contains a frame around it, and that it is sha1 encrypted, but my problem is that I have no idea how to read or remove this frame or unencrypt it.
Here is the full code of the web server so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Web;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Threading;
using System.Security.Cryptography;
namespace WebSocks
{
public class WebSockServer
{
/// <summary>
/// Port number to listen on
/// </summary>
private const int PortNumber = 8181;
/// <summary>
/// Socket which awaits connections
/// </summary>
private Socket ListenerSocket;
/// <summary>
/// Thread in which we await for incomming connections.
/// </summary>
private System.Threading.Thread _serverThread;
public delegate void ClientConnectedHandler (Socket Sock);
public delegate void ReceivedDataHandler(Socket Sock, string Message);
public event ClientConnectedHandler ClientConnected;
public event ReceivedDataHandler ReceivedData;
static WebSockServer() { }
/// <summary>
/// Starts thread with listening socket.
/// </summary>
public void Start()
{
System.Threading.ThreadStart ts = new System.Threading.ThreadStart(Listen);
_serverThread = new System.Threading.Thread(ts);
_serverThread.Start();
}
/// <summary>
/// Stops listening for connections.
/// </summary>
public void End()
{
_serverThread.Abort();
ListenerSocket.Dispose();
}
public void Listen()
{
//Start listening
ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
EndPoint ep = new IPEndPoint(IPAddress.Parse("0.0.0.0"), PortNumber);
ListenerSocket.Bind(ep);
ListenerSocket.Listen(5);
while (true)
{
//New client
Socket client = ListenerSocket.Accept();
//Receiving clientHandshake
string clientHandshake = String.Empty;
byte[] buffer = null;
int readBytes = 0;
do
{
buffer = new byte[client.Available];
readBytes = client.Receive(buffer);
clientHandshake += Encoding.UTF8.GetString(buffer);
}
while (client.Available > 0);
//Last eight bytes are body of requets (we should include it in response)
byte[] secKey3 = buffer.Skip(readBytes - 8).Take(8).ToArray();
//Variables we can extract from clientHandshake
string clientOrigin = String.Empty;
string secKey1 = String.Empty;
string secKey2 = String.Empty;
string WebSocketVersion = String.Empty;
int WSV = 0;
string WebSocketKey = String.Empty;
//Extracting values from headers (key:value)
string[] clientHandshakeLines = Regex.Split(clientHandshake, Environment.NewLine);
foreach (string hline in clientHandshakeLines)
{
int valueStartIndex = hline.IndexOf(':') + 2;
if (valueStartIndex > 0)
{
if (hline.StartsWith("Origin"))
{
clientOrigin = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
}
else if (hline.StartsWith("Sec-WebSocket-Key2"))
{
secKey2 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
}
else if (hline.StartsWith("Sec-WebSocket-Key1"))
{
secKey1 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
}
if (hline.StartsWith("Sec-WebSocket-Version"))
{
WebSocketVersion = hline.Replace("Sec-WebSocket-Version: ", "");
WSV = Convert.ToInt32(WebSocketVersion);
}
if (hline.StartsWith("Sec-WebSocket-Key"))
{
WebSocketKey = hline.Replace("Sec-WebSocket-Key: ", "");
}
}
}
if (!String.IsNullOrEmpty(WebSocketVersion)) //WebSocketVersion 8 and up handshake check
{
//New WebSocketVersion number, included after Version 8
StringBuilder mResponse = new StringBuilder();
mResponse.AppendLine("HTTP/1.1 101 Switching Protocols");
mResponse.AppendLine("Upgrade: WebSocket");
mResponse.AppendLine("Connection: Upgrade");
mResponse.AppendLine(String.Format("Sec-WebSocket-Accept: {0}", ComputeWebSocketHandshakeSecurityHash09(WebSocketKey)) + Environment.NewLine);
byte[] HSText = Encoding.UTF8.GetBytes(mResponse.ToString());
client.Send(HSText, 0, HSText.Length, 0);
}
else
{
//This part is common for all websockets editions (v. 75 & v.76)
client.Send(Encoding.UTF8.GetBytes("HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine));
client.Send(Encoding.UTF8.GetBytes("Upgrade: WebSocket" + Environment.NewLine));
client.Send(Encoding.UTF8.GetBytes("Connection: Upgrade" + Environment.NewLine));
if (String.IsNullOrEmpty(secKey1) && String.IsNullOrEmpty(secKey2)) //75 or less handshake check
{
client.Send(Encoding.UTF8.GetBytes(String.Format("WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine));
client.Send(Encoding.UTF8.GetBytes("WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine));
client.Send(Encoding.UTF8.GetBytes(Environment.NewLine));
}
else //76 handshake check
{
//Keys present, this means 76 version is used. Writing Sec-* headers
client.Send(Encoding.UTF8.GetBytes(String.Format("Sec-WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine));
client.Send(Encoding.UTF8.GetBytes("Sec-WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine));
client.Send(Encoding.UTF8.GetBytes(Environment.NewLine));
//Calculating response body
byte[] secret = CalculateSecurityBody(secKey1, secKey2, secKey3);
client.Send(secret);
}
}
if (ClientConnected != null)
{
ClientConnected(client);
}
Thread t = new Thread(new ParameterizedThreadStart(WaitForMessages));
t.Start(client);
}
}
private static void SendMessage(string Msg, Socket client, int WebSockVersion)
{
if (WebSockVersion >= 8)
{
bool IsFinal = true;
int OpCode = 1;
int? Mask = null;
byte[] payload = Encoding.UTF8.GetBytes(Msg);
int PayloadLength = payload.Length;
byte[] buffer = new byte[64]; // for working out the header
int offset = 0;
buffer[offset++] = (byte)((IsFinal ? 128 : 0) | ((int)OpCode & 15));
if (PayloadLength > ushort.MaxValue)
{ // write as a 64-bit length
buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127);
buffer[offset++] = 0;
buffer[offset++] = 0;
buffer[offset++] = 0;
buffer[offset++] = 0;
buffer[offset++] = (byte)(PayloadLength >> 24);
buffer[offset++] = (byte)(PayloadLength >> 16);
buffer[offset++] = (byte)(PayloadLength >> 8);
buffer[offset++] = (byte)(PayloadLength);
}
else if (PayloadLength > 125)
{ // write as a 16-bit length
buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126);
buffer[offset++] = (byte)(PayloadLength >> 8);
buffer[offset++] = (byte)(PayloadLength);
}
else
{ // write in the header
buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength);
}
if (Mask.HasValue)
{
int mask = Mask.Value;
buffer[offset++] = (byte)(mask >> 24);
buffer[offset++] = (byte)(mask >> 16);
buffer[offset++] = (byte)(mask >> 8);
buffer[offset++] = (byte)(mask);
}
// you might want to manually combine these into 1 packet
client.Send(buffer, 0, offset, SocketFlags.None);
client.Send(payload, 0, payload.Length, SocketFlags.None);
}
else
{
client.Send(new byte[] { 0x00 });
client.Send(Encoding.UTF8.GetBytes(Msg));
client.Send(new byte[] { 0xFF });
}
}
private void WaitForMessages(object client)
{
Socket sock = (Socket)client;
byte[] buffer = new byte[1024];
while (true)
{
sock.Receive(buffer);
ReceivedData(sock, Encoding.UTF8.GetString(buffer));
}
}
public byte[] CalculateSecurityBody(string secKey1, string secKey2, byte[] secKey3)
{
//Remove all symbols that are not numbers
string k1 = Regex.Replace(secKey1, "[^0-9]", String.Empty);
string k2 = Regex.Replace(secKey2, "[^0-9]", String.Empty);
//Convert received string to 64 bit integer.
Int64 intK1 = Int64.Parse(k1);
Int64 intK2 = Int64.Parse(k2);
//Dividing on number of spaces
int k1Spaces = secKey1.Count(c => c == ' ');
int k2Spaces = secKey2.Count(c => c == ' ');
int k1FinalNum = (int)(intK1 / k1Spaces);
int k2FinalNum = (int)(intK2 / k2Spaces);
//Getting byte parts
byte[] b1 = BitConverter.GetBytes(k1FinalNum).Reverse().ToArray();
byte[] b2 = BitConverter.GetBytes(k2FinalNum).Reverse().ToArray();
//byte[] b3 = Encoding.UTF8.GetBytes(secKey3);
byte[] b3 = secKey3;
//Concatenating everything into 1 byte array for hashing.
List<byte> bChallenge = new List<byte>();
bChallenge.AddRange(b1);
bChallenge.AddRange(b2);
bChallenge.AddRange(b3);
//Hash and return
byte[] hash = MD5.Create().ComputeHash(bChallenge.ToArray());
return hash;
}
public String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)
{
const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
String secWebSocketAccept = String.Empty;
// 1. Combine the request Sec-WebSocket-Key with magic key.
String ret = secWebSocketKey + MagicKEY;
// 2. Compute the SHA1 hash
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
// 3. Base64 encode the hash
secWebSocketAccept = Convert.ToBase64String(sha1Hash);
return secWebSocketAccept;
}
}
}
I know its very crude, it doesn't clean up after itself, and it doesn't separate the different connections properly and hold them in a list or anything, this is purely for a demonstration project.
So the main section I need help with is the WaitForMessages function:
private void WaitForMessages(object client)
{
Socket sock = (Socket)client;
byte[] buffer = new byte[1024];
while (true)
{
sock.Receive(buffer);
//Remove Frame and decrypt here
ReceivedData(sock, Encoding.UTF8.GetString(buffer));
}
}
I really just want to drop some code in here that will work, if you point me to a "demonstration that is kind of like your code and you should be able to figure it out from this" I'm really not going to understand it, I can almost guarantee you. I'm an app developer, not an API developer, I just don't want to wait until Microsoft finally gets around to writing an API into .NET 6.0 or something for this feature.