I have a TcpClient class on configuring client and server on my local machine. I use network stream to facilitate messaging between the two successfully.
Moving Forward I'm trying to implement compression in messages. I tried GZipStream and DeflateStream. I decided to focus on DeflateStream. However, the connection now hangs without reading data.
I tried 4 different implementations, which all failed due to the fact that the server side did not read the incoming data and the connection synchronization time. I will focus on two implementations that I have tried recently, and as far as I know, should work.
The client is divided into this request: there are 2 separate implementations, one of which is without a streamwriter.
textToSend = ENQUIRY + START_OF_TEXT + textToSend + END_OF_TEXT; // Send XML Request byte[] request = Encoding.UTF8.GetBytes(textToSend); using (DeflateStream streamOut = new DeflateStream(netStream, CompressionMode.Compress, true)) { //using (StreamWriter sw = new StreamWriter(streamOut)) //{ // sw.Write(textToSend); // sw.Flush(); streamOut.Write(request, 0, request.Length); streamOut.Flush(); //} }
The server receives the request, and I do 1.) a quick look at the first character, if it matches what I expect
2.) I continue to read the rest.
The first read works correctly, and if I want to read the entire stream, that’s it. However, I only want to read the first character and evaluate it, and then continue in the LongReadStream method.
When I try to continue reading the stream, there is no data to read. I assume that data is lost during the first read, but I'm not sure how to determine this. All this code works correctly when I use a regular NetworkStream.
Here is the server side code.
private void ProcessRequests() { // This method reads the first byte of data correctly and if I want to // I can read the entire request here. However, I want to leave // all that data until I want it below in my LongReadStream method. if (QuickReadStream(_netStream, receiveBuffer, 1) != ENQUIRY) { // Invalid Request, close connection clientIsFinished = true; _client.Client.Disconnect(true); _client.Close(); return; } while (!clientIsFinished) // Keep reading text until client sends END_TRANSMISSION { // Inside this method there is no data and the connection times out waiting for data receiveText = LongReadStream(_netStream, _client); // Continue talking with Client... } _client.Client.Shutdown(SocketShutdown.Both); _client.Client.Disconnect(true); _client.Close(); } private string LongReadStream(NetworkStream stream, TcpClient c) { bool foundEOT = false; StringBuilder sbFullText = new StringBuilder(); int readLength, totalBytesRead = 0; string currentReadText; c.ReceiveBufferSize = DEFAULT_BUFFERSIZE * 100; byte[] bigReadBuffer = new byte[c.ReceiveBufferSize]; while (!foundEOT) { using (var decompressStream = new DeflateStream(stream, CompressionMode.Decompress, true)) { //using (StreamReader sr = new StreamReader(decompressStream)) //{ //currentReadText = sr.ReadToEnd(); //} readLength = decompressStream.Read(bigReadBuffer, 0, c.ReceiveBufferSize); currentReadText = Encoding.UTF8.GetString(bigReadBuffer, 0, readLength); totalBytesRead += readLength; } sbFullText.Append(currentReadText); if (currentReadText.EndsWith(END_OF_TEXT)) { foundEOT = true; sbFullText.Length = sbFullText.Length - 1; } else { sbFullText.Append(currentReadText); } // Validate data code removed for simplicity } c.ReceiveBufferSize = DEFAULT_BUFFERSIZE; c.ReceiveTimeout = timeOutMilliseconds; return sbFullText.ToString(); } private string QuickReadStream(NetworkStream stream, byte[] receiveBuffer, int receiveBufferSize) { using (DeflateStream zippy = new DeflateStream(stream, CompressionMode.Decompress, true)) { int bytesIn = zippy.Read(receiveBuffer, 0, receiveBufferSize); var returnValue = Encoding.UTF8.GetString(receiveBuffer, 0, bytesIn); return returnValue; } }
EDIT NetworkStream has a Socket property that has an Available property. MSDN is talking about an existing property.
Gets the amount of data received from the network, and available for reading.
Before calling below, 77 is available. After reading 1 byte, the value is 0.
There seems to be no documentation that DeflateStream consumes the entire base stream, and I don't know why it will do such a thing when there are explicit calls that need to be made to read certain numbers of bytes.
Does anyone know why this is happening, or if there is a way to save basic data for future reading? Based on this “function” and the previous article I read , indicating that DeflateStream must be closed in order to finish sending (the flash will not work) DeflateStreams may be limited in use for networks, especially if you want to resist DOS attacks by checking incoming data before taking the full stream.