Ho una classe TcpClient su un'installazione client e server sul mio computer locale. Ho utilizzato il flusso di rete per facilitare le comunicazioni avanti e indietro tra i 2 con successo.Perché il mio DeflateStream non riceve correttamente i dati tramite TCP?
In avanti Sto tentando di implementare la compressione nelle comunicazioni. Ho provato GZipStream e DeflateStream. Ho deciso di concentrarmi su DeflateStream. Tuttavia, la connessione si blocca senza leggere i dati ora.
Ho provato 4 diverse implementazioni che hanno tutte fallito a causa del lato server che non leggeva i dati in entrata e il timeout della connessione. Mi concentrerò sulle due implementazioni che ho provato più di recente e, a mia conoscenza, dovrebbe funzionare.
Il client è suddiviso in base a questa richiesta: Esistono 2 implementazioni separate, una con streamwriter una senza.
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();
//}
}
Il server riceve la richiesta e lo faccio
1.) una rapida lettura del primo carattere, quindi se corrisponde a quello che mi aspetto
2.) Io continuo a leggere il resto.
La prima lettura funziona correttamente e se voglio leggere l'intero flusso è tutto lì. Tuttavia, desidero solo leggere il primo carattere e valutarlo, quindi continuare nel mio metodo LongReadStream.
Quando provo a continuare a leggere lo stream non ci sono dati da leggere. Immagino che i dati vengano persi durante la prima lettura, ma non sono sicuro di come determinarlo. Tutto questo codice funziona correttamente quando utilizzo il normale NetworkStream.
Ecco il codice lato server.
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 ha una proprietà Presa di fondo che ha una proprietà disponibile. MSDN dice questo sulla proprietà disponibile.
Ottiene la quantità di dati che è stata ricevuta dalla rete ed è disponibile per la lettura.
Prima della chiamata di seguito disponibile è 77. Dopo aver letto 1 byte il valore è 0.
//receiveBufferSize = 1
int bytesIn = zippy.Read(receiveBuffer, 0, receiveBufferSize);
Non sembra esserci alcuna documentazione su DeflateStream consumando l'intero flusso sottostante e io don' so perché farebbe una cosa del genere quando ci sono chiamate esplicite da fare per leggere numeri specifici di byte.
Qualcuno sa perché questo succede o se c'è un modo per preservare i dati sottostanti per una lettura futura? Sulla base di questa 'caratteristica' e di una precedente affermando che un DeflateStream deve essere chiuso per terminare l'invio (flush non funzionerà) sembra che DeflateStreams possa essere limitato nel loro uso per la rete specialmente se si vuole contrastare gli attacchi DOS testando i dati in arrivo prima accettare un flusso completo.
Appena speculare, ma forse il costruttore di DeflateStream in QuickReadStream rileva che NetworkStream è un inoltro solo lettura sola flusso e la lettura dell'intera cosa in zippy. Quindi leggi il primo byte da zippy, imposta il tuo returnValue e ritorna. Quando zippy esce dal campo di applicazione non c'è nulla da leggere in NetworkStream perché è già stato letto e disgustato. – Kevin
Ho aggiornato la mia domanda sulla base del tuo commento apparentemente corretto. Questo è fondamentalmente problematico come notato nella mia domanda. –
La mitigazione del DOS viene in genere eseguita a livello di appliance di rete, in cui i dati del flusso possono essere monitorati/analizzati al loro interno. Dal punto di vista dell'applicazione, non sono sicuro che ci sia molto da fare per rilevare o mitigare un simile attacco. Il mio unico suggerimento sarebbe quello di leggere il flusso nella sua interezza in un MemoryStream, testarlo, quindi scartare se non è giusto. Ciò non ridurrebbe il carico di traffico TCP di un attacco, ma potrebbe impedire l'elaborazione/archiviazione non necessaria dei dati sui rifiuti. – Kevin