How to save a MJPEG Stream to disk (C# .NET)? - c#

I have an application that read the stream from a camera (MJPEG) and show it on the form in real time (in a picture box). This is working. This stream reading start when the user click on the button "Start".
What I want to do is that when the user click on a button "Stop", the stream between the button "Start" and "Stop" would be save on disk as a .mpg.
Right now, it write something on the disk, but I can't open it in Windows Media Player.
Here is the code to write the stream
private void ReadWriteStream(byte[] buffer, int start, int lenght, Stream writeStream)
{
Stream readStream = new MemoryStream(buffer, start, lenght);
int bytesRead = readStream.Read(buffer, 0, m_readSize);
// write the required bytes
while (bytesRead > 0 && !m_bStopLecture)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, m_readSize);
}
readStream.Close();
}
Here is the place that call the function. This is in a loop and as I said, the video is playing in the PictureBox.
// image at stop
Stream towrite = new MemoryStream(buffer, start, stop - start);
Image img = Image.FromStream(towrite);
imgSnapshot.Image = img;
// write to the stream
ReadWriteStream(buffer, start, stop - start, writeStream);
Thanks a lot!

You need to set the content type on the stream, and include the frame boundry data. I would start by looking at the question MJPG VLC and HTTP Streaming.

There is an implementation # https://net7mma.codeplex.com/SourceControl/latest specifically https://net7mma.codeplex.com/SourceControl/latest#Rtsp/Server/Streams/MJPEGSourceStream.cs
Something like this:
{
// buffer to read stream
byte[] buffer = new byte[bufSize];
// JPEG magic number
byte[] jpegMagic = new byte[] { 0xFF, 0xD8, 0xFF };
int jpegMagicLength = 3;
ASCIIEncoding encoding = new ASCIIEncoding();
while (!stopEvent.WaitOne(0, false))
{
// reset reload event
reloadEvent.Reset();
// HTTP web request
HttpWebRequest request = null;
// web responce
WebResponse response = null;
// stream for MJPEG downloading
Stream stream = null;
// boundary betweeen images (string and binary versions)
byte[] boundary = null;
string boudaryStr = null;
// length of boundary
int boundaryLen;
// flag signaling if boundary was checked or not
bool boundaryIsChecked = false;
// read amounts and positions
int read, todo = 0, total = 0, pos = 0, align = 1;
int start = 0, stop = 0;
// align
// 1 = searching for image start
// 2 = searching for image end
try
{
// create request
request = (HttpWebRequest)WebRequest.Create(m_Source);
// set user agent
if (userAgent != null)
{
request.UserAgent = userAgent;
}
// set proxy
if (proxy != null)
{
request.Proxy = proxy;
}
// set timeout value for the request
request.Timeout = requestTimeout;
// set login and password
if ((login != null) && (password != null) && (login != string.Empty))
request.Credentials = new NetworkCredential(login, password);
// set connection group name
if (useSeparateConnectionGroup)
request.ConnectionGroupName = GetHashCode().ToString();
// force basic authentication through extra headers if required
if (forceBasicAuthentication)
{
string authInfo = string.Format("{0}:{1}", login, password);
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
request.Headers["Authorization"] = "Basic " + authInfo;
}
// get response
response = request.GetResponse();
// check content type
string contentType = response.ContentType;
string[] contentTypeArray = contentType.Split('/');
// "application/octet-stream"
if ((contentTypeArray[0] == "application") && (contentTypeArray[1] == "octet-stream"))
{
boundaryLen = 0;
boundary = new byte[0];
}
else if ((contentTypeArray[0] == "multipart") && (contentType.Contains("mixed")))
{
// get boundary
int boundaryIndex = contentType.IndexOf("boundary", 0);
if (boundaryIndex != -1)
{
boundaryIndex = contentType.IndexOf("=", boundaryIndex + 8);
}
if (boundaryIndex == -1)
{
// try same scenario as with octet-stream, i.e. without boundaries
boundaryLen = 0;
boundary = new byte[0];
}
else
{
boudaryStr = contentType.Substring(boundaryIndex + 1);
// remove spaces and double quotes, which may be added by some IP cameras
boudaryStr = boudaryStr.Trim(' ', '"');
boundary = encoding.GetBytes(boudaryStr);
boundaryLen = boundary.Length;
boundaryIsChecked = false;
}
}
else
{
throw new Exception("Invalid content type.");
}
// get response stream
stream = response.GetResponseStream();
stream.ReadTimeout = requestTimeout;
// loop
while ((!stopEvent.WaitOne(0, false)) && (!reloadEvent.WaitOne(0, false)))
{
// check total read
if (total > bufSize - readSize)
{
total = pos = todo = 0;
}
// read next portion from stream
if ((read = stream.Read(buffer, total, readSize)) == 0)
throw new ApplicationException();
total += read;
todo += read;
// increment received bytes counter
bytesReceived += read;
// do we need to check boundary ?
if ((boundaryLen != 0) && (!boundaryIsChecked))
{
// some IP cameras, like AirLink, claim that boundary is "myboundary",
// when it is really "--myboundary". this needs to be corrected.
pos = Utility.ContainsBytes(buffer, ref start, ref read, boundary, 0, boundary.Length);
// continue reading if boudary was not found
if (pos == -1)
continue;
for (int i = pos - 1; i >= 0; i--)
{
byte ch = buffer[i];
if ((ch == (byte)'\n') || (ch == (byte)'\r'))
{
break;
}
boudaryStr = (char)ch + boudaryStr;
}
boundary = encoding.GetBytes(boudaryStr);
boundaryLen = boundary.Length;
boundaryIsChecked = true;
}
// search for image start
if ((align == 1) && (todo >= jpegMagicLength))
{
start = Utility.ContainsBytes(buffer, ref pos, ref todo, jpegMagic, 0, jpegMagicLength);
if (start != -1)
{
// found JPEG start
pos = start + jpegMagicLength;
todo = total - pos;
align = 2;
}
else
{
// delimiter not found
todo = jpegMagicLength - 1;
pos = total - todo;
}
}
// search for image end ( boundaryLen can be 0, so need extra check )
while ((align == 2) && (todo != 0) && (todo >= boundaryLen))
{
stop = Utility.ContainsBytes(buffer, ref start, ref read,
(boundaryLen != 0) ? boundary : jpegMagic,
pos, todo);
if (stop != -1)
{
pos = stop;
todo = total - pos;
// increment frames counter
framesReceived++;
// image at stop
using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, start, stop - start)))
{
// notify client
Packetize(bitmap);
}
// shift array
pos = stop + boundaryLen;
todo = total - pos;
Array.Copy(buffer, pos, buffer, 0, todo);
total = todo;
pos = 0;
align = 1;
}
else
{
// boundary not found
if (boundaryLen != 0)
{
todo = boundaryLen - 1;
pos = total - todo;
}
else
{
todo = 0;
pos = total;
}
}
}
}
}
catch (ApplicationException)
{
// do nothing for Application Exception, which we raised on our own
// wait for a while before the next try
Thread.Sleep(250);
}
catch (ThreadAbortException)
{
break;
}
catch (Exception exception)
{
// wait for a while before the next try
Thread.Sleep(250);
}
finally
{
// abort request
if (request != null)
{
request.Abort();
request = null;
}
// close response stream
if (stream != null)
{
stream.Close();
stream = null;
}
// close response
if (response != null)
{
response.Close();
response = null;
}
}
// need to stop ?
if (stopEvent.WaitOne(0, false))
break;
}
}
}

Related

Building a large file text-editor

So I'm building my own custom-drawn large text edit control, which I want to be able to open a large text file, and only a small chunk of it. I am able to successfully do that, but the problem is large lines. I have it so that every time I read a char (which shouldn't be too many as it's limited to the size of the screen), it will measure the length of that line. If it's greater than the width of the screen, it will stop rendering that line. However, I still have to find the position of the next line which means having to read that line (which could be hundreds of GB large). Here is my code so far:
if (encoding == Encoding.ASCII || encoding == Encoding.Default)
{
fileStream.Seek(visibleLinesRanges[0].Start, SeekOrigin.Begin);
int maxNumberLines = MaxLinesOnScreen;
int linesRead = 0;
int read;
byte[] byteBuffer = new byte[bufferSize];
StringBuilder text = new StringBuilder();
string lineText = string.Empty;
bool dontAddLine = false;
void AddLine()
{
if (dontAddLine)
{
lineText = string.Empty;
return;
}
linesRead++;
bool visible = linesRead >= maxNumberLines;
if (vScrollBar.Visible != visible)
{
vScrollBar.Visible = visible;
}
text.Append(lineText);
lineText = string.Empty;
}
while ((read = fileStream.Read(byteBuffer, 0, bufferSize)) > 0)
{
char[] charBuffer = encoding.GetChars(byteBuffer, 0, read);
for (int i = 0; i < read; i++)
{
char c = charBuffer[i];
if (!dontAddLine)
{
string newLineText = lineText + c;
if (TextRenderer.MeasureText(newLineText, Font).Width > Width)
{
lineText += "\n";
AddLine();
if (linesRead > maxNumberLines)
{
goto Drawing;
}
dontAddLine = true;
}
else
{
lineText = newLineText;
}
}
if (c == '\r')
{
char c1 = ReadCharSingle(charBuffer, i, read);
if (c1 != '\n')
{
AddLine();
if (linesRead > maxNumberLines)
{
goto Drawing;
}
dontAddLine = false;
}
}
else if (c == '\n')
{
AddLine();
if (linesRead > maxNumberLines)
{
goto Drawing;
}
dontAddLine = false;
}
}
}
Drawing:
TextRenderer.DrawText(g, text.ToString(), Font, Point.Empty, Color.Gainsboro);
}
This works, and barely uses any memory, but the problem is that it's very slow when opening a file with a single line that is about ~120MB in size. Also, whenever I resize the window, I have to call this code (in the Paint event), so that the text is updated.
This is very slow. Is there any way to speed this up? Thanks.

Try to read data from plc s7 1200 with snap 7 and get the wrong string

I try to read data from datablock (DB60), but I get only ?5. So in data block should be JAMES17.
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text == "Connect PLC")
{
button1.Text = "Disconnect PLC";
ClassPLCS7Client.PLCClientConnect_Result ConnectResult = new
ClassPLCS7Client.PLCClientConnect_Result();
ConnectResult = PLCClient.Connect(("192.168.0.2"), 0, 1);
if (ConnectResult.State == ClassPLCS7Client.PLCClientConnectState.Connected)
{
this.label1.Text = "Connected PLC1 " + ConnectResult.ReSultString;
label1.ForeColor = Color.Green;
ClassPLCS7Client.ReadDataBlockString_Result read = new ClassPLCS7Client.ReadDataBlockString_Result();
read = PLCClient.ReadDataBlockString(60, 0, 7);
this.textBox1.Text = read.DataValue[0];
//this.textBox1.Text = arr4[];// read.ReSultString;
}
else
{
this.label1.Text = "Fail " + ConnectResult.ReSultString;
label1.ForeColor = Color.Red;
}
}
else
{
button1.Text = "Connect PLC";
disconnect_plc();
this.label1.Text = "Disconnect";
label1.ForeColor = Color.Black;
}
}<code>
First : you are putting J string in byte offset 0 and A string in byte offset 256 and M string in byte offset 512 etcetera .... you don't have a string in consecutive bytes as it should be.
Second: when S7 stores a string the two first bytes are reserved to store the first the max string size in bytes and the second the actual size of string. Therefore in your case your memory must contain this: (Assuming the reserved memory is 256 bytes size)
offset 0 == 254, offset 1 == 7, offset 2 to 8 == 'JAMES17'
From i try to change DB60 to string "JAMES17"
the result is
https://www.dropbox.com/s/awdadp53tuyszpe/2.PNG?dl=1
I found this code in the class ReadDataBlockString_Result. So i call this function and not sure how to use it.
public ReadDataBlockString_Result ReadDataBlockString(int DataBlockNumber,
int StartAddress, int LenghtOfRead)
{
ReadDataBlockString_Result rt = new ReadDataBlockString_Result();
rt.MemoryByte = new byte[256];
//if (this.State == PLCClientConnectState.Connected)
//{
rt.DataValue = new string[LenghtOfRead];
int i = 0;
int CaptureIndex = StartAddress;
for (i = 0; i <= LenghtOfRead - 1; i++)
{
rt.ResultCode = PLCClient.DBRead(DataBlockNumber, CaptureIndex, 256, rt.MemoryByte);
CaptureIndex = CaptureIndex + 256;
if (rt.ResultCode == 0)
{
string Convertedvalue = null;
Convertedvalue = System.Text.Encoding.ASCII.GetString(rt.MemoryByte);
rt.DataValue[i] = Convertedvalue;
}
}
//}
//else
//{
// rt.ResultCode = -1;
//}
rt.ReSultString = PLCClient.ErrorText(rt.ResultCode);
return rt;
}

C# TcpClient streamreader with eventhandler not all messages are processed

I'm reading continuously from a TcpClient streamreader.
The data coming from the stream is raw XML. There is no message framing. So there is now reliable method to know when the message is finished. Though I only have 3 XML messages coming from the server. But when they are coming is unknown. And I can't configure/program the server.
This is my code so far.
public void Start()
{
StreamReader reader = new StreamReader(_tcpClient.GetStream());
char[] chars = new char[Int16.MaxValue];
while (!_requestStop)
{
try
{
while ((reader.Read(chars, 0, chars.Length)) != 0)
{
string s = new string(chars);
s = removeEmptyChars(s);
if (s.IndexOf("<foo", StringComparison.OrdinalIgnoreCase) > 0 &&
s.IndexOf("</foo>", StringComparison.OrdinalIgnoreCase) > 0)
{
Console.WriteLine(s);
OnAlarmResponseComplete(new CustomEventArgs(s));
}
if (s.IndexOf("<bar", StringComparison.OrdinalIgnoreCase) > 0 &&
s.IndexOf("</bar>", StringComparison.OrdinalIgnoreCase) > 0)
{
Console.WriteLine(s);
OnAckComplete(new CustomEventArgs(s));
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
//break;
}
}
reader.Close();
Console.WriteLine("Stopping TcpReader thread!");
}
Then in my main thread I'm processing the events. I'm adding them to a list.
Where I process the list.
When I'm debugging my application, I will be receiving 10 foo and 10 bar messages. And in my lists I have only 1 foo and 1 bar message stored.
Are the eventhandlers to slow to process this? Or am I missing something?
Here is the code you should use to cover all kinds of input issues (foo or bar received partially, foo and bar received together, etc..)
I can't say I approve using string parsing to handle XML content, but anyways.
private static string ProcessAndTrimFooBar(string s, out bool foundAny)
{
foundAny = false;
int fooStart = s.IndexOf("<foo", StringComparison.OrdinalIgnoreCase);
int fooEnd = s.IndexOf("</foo>", StringComparison.OrdinalIgnoreCase);
int barStart = s.IndexOf("<bar", StringComparison.OrdinalIgnoreCase);
int barEnd = s.IndexOf("</bar>", StringComparison.OrdinalIgnoreCase);
bool fooExists = fooStart >= 0 && fooEnd >= 0;
bool barExists = barStart >= 0 && barEnd >= 0;
if ((fooExists && !barExists) || (fooExists && barExists && fooStart < barStart))
{
string fooNodeContent = s.Substring(fooStart, fooEnd - fooStart + 6);
s = s.Substring(fooEnd + 6);
Console.WriteLine("Received <foo>: {0}", fooNodeContent);
OnAlarmResponseComplete(new CustomEventArgs(fooNodeContent));
foundAny = true;
}
if ((barExists && !fooExists) || (barExists && fooExists && barStart < fooStart))
{
string barNodeContent = s.Substring(barStart, barEnd - barStart + 6);
s = s.Substring(barEnd + 6);
Console.WriteLine("Received <bar>: {0}", barNodeContent);
OnAckComplete(new CustomEventArgs(barNodeContent));
foundAny = true;
}
return s;
}
public static void Start()
{
StreamReader reader = new StreamReader(_tcpClient.GetStream());
char[] chars = new char[Int16.MaxValue];
while (!_requestStop)
{
try
{
int currentOffset = 0;
while ((reader.Read(chars, currentOffset, chars.Length - currentOffset)) != 0)
{
string s = new string(chars).TrimEnd('\0');
bool foundAny;
do
{
s = ProcessAndTrimFooBar(s, out foundAny);
} while (foundAny);
chars = s.PadRight(Int16.MaxValue, '\0').ToCharArray();
currentOffset = s.Length;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
//break;
}
}
reader.Close();
Console.WriteLine("Stopping TcpReader thread!");
}

Streamreader with custom LineBreak - Performance optimisation

Edit: See my Solution below...
I had the following Problem to solve:
We receive Files (mostly adress-Information) from different sources, these can be in Windows Standard with CR/LF ('\r''\n') as Line Break or UNIX with LF ('\n').
When reading text in using the StreamReader.ReadLine() method, this is no Problem because it handles both cases equally.
The Problem occurs when you have a CR or a LF somewhere in the File that is not supposed to be there.
This happens for example if you Export a EXCEL-File with Cells that contain LineBreaks within the Cell to .CSV or other Flat-Files.
Now you have a File that for example has the following structure:
FirstName;LastName;Street;HouseNumber;PostalCode;City;Country'\r''\n'
Jane;Doe;co James Doe'\n'TestStreet;5;TestCity;TestCountry'\r''\n'
John;Hancock;Teststreet;1;4586;TestCity;TestCounty'\r''\n'
Now the StreamReader.ReadLine() Method reads the First Line as:
FirstName;LastName;Street;HouseNumber;PostalCode;City;Country
Which is fine but the seccond Line will be:
Jane;Doe;co James Doe
This will either break your Code or you will have false Results, as the following Line will be:
TestStreet;5;TestCity;TestCountry
So we usualy ran the File trough a tool that checks if there are loose '\n' or '\r' arround and delete them.
But this step is easy to Forget and so I tried to implement a ReadLine() method of my own. The requirement was that it would be able to use one or two LineBreak characters and those characters could be defined freely by the consuming logic.
This is the Class that I came up with:
public class ReadFile
{
private FileStream file;
private StreamReader reader;
private string fileLocation;
private Encoding fileEncoding;
private char lineBreak1;
private char lineBreak2;
private bool useSeccondLineBreak;
private bool streamCreated = false;
private bool endOfStream;
public bool EndOfStream
{
get { return endOfStream; }
set { endOfStream = value; }
}
public ReadFile(string FileLocation, Encoding FileEncoding, char LineBreak1, char LineBreak2, bool UseSeccondLineBreak)
{
fileLocation = FileLocation;
fileEncoding = FileEncoding;
lineBreak1 = LineBreak1;
lineBreak2 = LineBreak2;
useSeccondLineBreak = UseSeccondLineBreak;
}
public string ReadLine()
{
if (streamCreated == false)
{
file = new FileStream(fileLocation, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
reader = new StreamReader(file, fileEncoding);
streamCreated = true;
}
StringBuilder builder = new StringBuilder();
char[] buffer = new char[1];
char lastChar = new char();
char currentChar = new char();
bool first = true;
while (reader.EndOfStream != true)
{
if (useSeccondLineBreak == true)
{
reader.Read(buffer, 0, 1);
lastChar = currentChar;
if (currentChar == lineBreak1 && buffer[0] == lineBreak2)
{
break;
}
else
{
currentChar = buffer[0];
}
if (first == false)
{
builder.Append(lastChar);
}
else
{
first = false;
}
}
else
{
reader.Read(buffer, 0, 1);
if (buffer[0] == lineBreak1)
{
break;
}
else
{
currentChar = buffer[0];
}
builder.Append(currentChar);
}
}
if (reader.EndOfStream == true)
{
EndOfStream = true;
}
return builder.ToString();
}
public void Close()
{
if (streamCreated == true)
{
reader.Close();
file.Close();
}
}
}
This code works fine, it does what it is supposed to do but compared to the original StreamReader.ReadLine() method, it is ~3 Times slower. As we work with large row-Counts the difference is not only messured but also reflected in real world Performance.
(for 700'000 Rows it takes ~ 5 Seconds to read all Lines, extract a Chunk and write it to a new File, with my method it takes ~15 Seconds on my system)
I tried different aproaches with bigger buffers but so far I wasn't able to increase Performance.
What I would be interessted in:
Any suggestions how I could improve the performance of this code to get closer to the original Performance of StreamReader.ReadLine()?
Solution:
This now takes ~6 Seconds (compared to ~5 Sec using the Default 'StreamReader.ReadLine()' ) for 700'000 Rows to do the same things as the code above does.
Thanks Jim Mischel for pointing me in the right direction!
public class ReadFile
{
private FileStream file;
private StreamReader reader;
private string fileLocation;
private Encoding fileEncoding;
private char lineBreak1;
private char lineBreak2;
private bool useSeccondLineBreak;
const int BufferSize = 8192;
int bufferedCount;
char[] rest = new char[BufferSize];
int position = 0;
char lastChar;
bool useLastChar;
private bool streamCreated = false;
private bool endOfStream;
public bool EndOfStream
{
get { return endOfStream; }
set { endOfStream = value; }
}
public ReadFile(string FileLocation, Encoding FileEncoding, char LineBreak1, char LineBreak2, bool UseSeccondLineBreak)
{
fileLocation = FileLocation;
fileEncoding = FileEncoding;
lineBreak1 = LineBreak1;
lineBreak2 = LineBreak2;
useSeccondLineBreak = UseSeccondLineBreak;
}
private int readInBuffer()
{
return reader.Read(rest, 0, BufferSize);
}
public string ReadLine()
{
StringBuilder builder = new StringBuilder();
bool lineFound = false;
if (streamCreated == false)
{
file = new FileStream(fileLocation, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 8192);
reader = new StreamReader(file, fileEncoding);
streamCreated = true;
bufferedCount = readInBuffer();
}
while (lineFound == false && EndOfStream != true)
{
if (position < bufferedCount)
{
for (int i = position; i < BufferSize; i++)
{
if (useLastChar == true)
{
useLastChar = false;
if (rest[i] == lineBreak2)
{
count++;
position = i + 1;
lineFound = true;
break;
}
else
{
builder.Append(lastChar);
}
}
if (rest[i] == lineBreak1)
{
if (useSeccondLineBreak == true)
{
if (i + 1 <= BufferSize - 1)
{
if (rest[i + 1] == lineBreak2)
{
position = i + 2;
lineFound = true;
break;
}
else
{
builder.Append(rest[i]);
}
}
else
{
useLastChar = true;
lastChar = rest[i];
}
}
else
{
position = i + 1;
lineFound = true;
break;
}
}
else
{
builder.Append(rest[i]);
}
position = i + 1;
}
}
else
{
bufferedCount = readInBuffer();
position = 0;
}
}
if (reader.EndOfStream == true && position == bufferedCount)
{
EndOfStream = true;
}
return builder.ToString();
}
public void Close()
{
if (streamCreated == true)
{
reader.Close();
file.Close();
}
}
}
The way to speed this up would be to have it read more than one character at a time. For example, create a 4 kilobyte buffer, read data into that buffer, and then go character-by-character. If you copy character-by-character to a StringBuilder, it's pretty easy.
The code below shows how to parse out lines in a loop. You'd have to split this up so that it can maintain state between calls, but it should give you the idea.
const int BufferSize = 4096;
const string newline = "\r\n";
using (var strm = new StreamReader(....))
{
int newlineIndex = 0;
var buffer = new char[BufferSize];
StringBuilder sb = new StringBuilder();
int charsInBuffer = 0;
int bufferIndex = 0;
char lastChar = (char)-1;
while (!(strm.EndOfStream && bufferIndex >= charsInBuffer))
{
if (bufferIndex > charsInBuffer)
{
charsInBuffer = strm.Read(buffer, 0, buffer.Length);
if (charsInBuffer == 0)
{
// nothing read. Must be at end of stream.
break;
}
bufferIndex = 0;
}
if (buffer[bufferIndex] == newline[newlineIndex])
{
++newlineIndex;
if (newlineIndex == newline.Length)
{
// found a line
Console.WriteLine(sb.ToString());
newlineIndex = 0;
sb = new StringBuilder();
}
}
else
{
if (newlineIndex > 0)
{
// copy matched newline characters
sb.Append(newline.Substring(0, newlineIndex));
newlineIndex = 0;
}
sb.Append(buffer[bufferIndex]);
}
++bufferIndex;
}
// Might be a line left, without a newline
if (newlineIndex > 0)
{
sb.Append(newline.Substring(0, newlineIndex));
}
if (sb.Length > 0)
{
Console.WriteLine(sb.ToString());
}
}
You could optimize this a bit by keeping track of the starting position so that when you find a line you create a string from buffer[start] to buffer[current], without creating a StringBuilder. Instead you call the String(char[], int32, int32) constructor. That's a little tricky to handle when you cross a buffer boundary. Probably would want to handle crossing the buffer boundary as a special case and use a StringBuilder for temporary storage in that case.
I wouldn't bother with that optimization, though, until after I got this first version working.

Multithread with TCPclient

I do have a problem with a multithreaded TCPClient application, every Client object has a Thread that recievies and sends messages and a thread that handle the Tasks that should be handled (depending on the messages)... (for example creates and answer that the msg threads sends). But something is going wrong... the application almost allways uses 100% cpu (if any thread have a task, and thats most of the time). I also have a feeling that some threads get prioritised less then others (can see in some loggs that an operation takes longer in thread 1 then in thread 2 for exampel... Is there any nice way to handel this problem?
I would love some help or some hints!
Anything unclear just ask :)
Thanks! /Nick
//Waiting for TCP-connections and creating them as they arrive. Starts a Thread that handles the messages recieved and sent with this thread.
private void ListenForClients()
{
try
{
this.tcpListener.Start();
while (true)
{
TcpClient client = this.tcpListener.AcceptTcpClient();
Connection c = new Connection(this.parent);
connectionCollection.Add(c);
Thread clientThread = new Thread(new ParameterizedThreadStart(c.HandleClientComm));
threadCollection.Add(clientThread);
clientThread.Start(client);
}
}
catch (Exception e)
{
}
}
//Connection constructor, creates a ToDo-thread, this handles the messages (DB, Filewriting etc.) recieived and creats new ones to be sent.
public Connection()
{
this.todo = new ArrayList();
todoT = new Thread(handleToDo);
todoT.Start();
}
//Messagehandeling-thread
public void HandleClientComm(object client)
{
try
{
TcpClient server = (TcpClient)client;
NetworkStream ns = server.GetStream();
byte[] data = new byte[1024];
string input, stringData;
online = true;
DateTime lastTime = DateTime.Now;
while (true && this.online)
{
try
{
if (lastTime.AddMinutes(2) < DateTime.Now)
break;
data = new byte[1024];
if (ns.DataAvailable && ns.CanRead)
{
int recv = ns.Read(data, 0, data.Length);
if (recv > 0)
{
lastTime = DateTime.Now;
if ((byte)data[recv - 1] == (byte)255)
{
int cnt = -1;
for (int i = 0; i < recv; i++)
{
if (data[i] == (byte)254)
{
cnt = i;
break;
}
}
int nr = recv - cnt - 2;
byte[] tmp = new byte[nr];
for (int i = 0; i < nr; i++)
{
tmp[i] = data[cnt + i + 1];
}
string crc = Encoding.UTF8.GetString(tmp);
stringData = Encoding.UTF8.GetString(data, 0, cnt);
MsgStruct msgs = new MsgStruct(stringData);
msgs.setCrc(crc);
addTodo(msgs);
if (msgs.getMsg()[0] == 'T' && this.type == 1)
this.parent.cStructHandler.sendAck(msgs, this.ID);
Console.WriteLine(todo.Count);
}
}
}
if (parent.cStructHandler.gotMsg(this.ID))
{
MsgStruct tmpCs = parent.cStructHandler.getNextMsg(this.ID);
if (tmpCs.getMsg().Length != 0 && ns.CanWrite)
{
byte[] ba = Encoding.UTF8.GetBytes(tmpCs.getMsg());
if (tmpCs.getCrc() == "")
{
ulong tmp = CRC.calc_crc(ba, ba.Length);
tmpCs.setCrc(tmp.ToString("X"));
}
if (tmpCs.canSendByTimeout())
{
string crcStr = "?" + tmpCs.getCrc() + "?";
byte[] bb = Encoding.UTF8.GetBytes(crcStr);
crcStr = Encoding.UTF8.GetString(bb);
byte[] fullMsg = new byte[ba.Length + bb.Length];
bb[0] = 254;
bb[bb.Length - 1] = 255;
ba.CopyTo(fullMsg, 0);
bb.CopyTo(fullMsg, ba.Length);
string s = System.Text.UTF8Encoding.ASCII.GetString(fullMsg);
ns.Write(fullMsg, 0, fullMsg.Length);
if (!tmpCs.isAckNeeded())
parent.cStructHandler.removeNextMsg(this.ID);
}
}
}
}
catch (Exception e)
{
break;
}
}
ns.Close();
server.Close();
dead = true;
}
catch (Exception e)
{
dead = true;
}
}
//Todo-thread
public void handleToDo()
{
try
{
int cnt = 0;
while (true)
{
if (todo.Count > 0)
{
//SWITCH CASE FOR DIFFERENT MESSAGE TYPES, DOING TASKS DEPENDING ON WHAT ONES...
}
else
{
if (dead)
{
todoT.Abort();
todoT = null;
break;
}
}
}
}
}
Stop checking if data is available etc. and just let the read() block. That's how it's supposed to work!
If you want to write stuff to the socket, do it from another thread, (direct from parent thingy?), or change your design to use async reads/writes.
Looping around, polling stuff, sleep() etc. is just a waste of CPU and/or adding avoidable latency to your app.

Categories

Resources