J'ai une classe TcpClient sur une configuration client et serveur sur ma machine locale. J'ai utilisé le flux de réseau pour faciliter les communications entre les 2 avec succès. Aller de l'avant J'essaie d'implémenter la compression dans les communications. J'ai essayé GZipStream et DeflateStream. J'ai décidé de me concentrer sur DeflateStream. Cependant, la connexion est suspendue sans lire les données maintenant.Pourquoi mon DeflateStream ne reçoit-il pas correctement les données via TCP?
J'ai essayé 4 implémentations différentes qui ont toutes échoué parce que le côté serveur ne lisait pas les données entrantes et que le délai de connexion était dépassé. Je vais me concentrer sur les deux implémentations que j'ai essayées le plus récemment et à ma connaissance devraient fonctionner.
Le client est décomposé à cette demande: Il existe 2 implémentations distinctes, une avec un streamwriter un sans.
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();
//}
}
Le serveur reçoit la demande et je ne
1.) une lecture rapide du premier caractère alors si elle correspond à ce que j'attends
2.) Je continue à lire le reste.
La première lecture fonctionne correctement et si je veux lire le flux entier, tout est là. Cependant, je veux seulement lire le premier caractère et l'évaluer puis continuer dans ma méthode LongReadStream. Lorsque j'essaie de continuer à lire le flux, il n'y a pas de données à lire. Je devine que les données sont perdues pendant la première lecture mais je ne suis pas sûr de savoir comment déterminer cela. Tout ce code fonctionne correctement lorsque j'utilise le NetworkStream normal.
Voici le code côté serveur.
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 a une propriété Socket sous-jacente qui a une propriété disponible. MSDN dit ceci à propos de la propriété disponible.
Obtient la quantité de données qui a été reçue du réseau et est disponible pour être lu.
Avant l'appel ci-dessous est disponible 77. Après avoir lu 1 octet la valeur est 0.
//receiveBufferSize = 1
int bytesIn = zippy.Read(receiveBuffer, 0, receiveBufferSize);
Il ne semble pas y avoir de la documentation sur l'ensemble DeflateStream consommer flux sous-jacent et je n » Je ne sais pas pourquoi il ferait une telle chose quand il y a des appels explicites à faire pour lire des nombres spécifiques d'octets.
Est-ce que quelqu'un sait pourquoi cela se produit ou s'il y a un moyen de préserver les données sous-jacentes pour une lecture future? Basé sur cette 'fonctionnalité' et un précédent article that I read indiquant qu'un DeflateStream doit être fermé pour terminer l'envoi (flush ne fonctionnera pas) il semble que DeflateStreams peut être limité dans leur utilisation pour le réseautage surtout si l'on veut contrer les attaques DOS en testant les données avant accepter un flux complet.
Juste spéculant, mais peut-être le constructeur de DeflateStream en QuickReadStream détecte que NetworkStream est un avant uniquement en lecture seule flux et lire toute la chose en zippy. Vous lisez ensuite le premier octet de zippy, définissez votre returnValue et revenez. Lorsque zippy est hors de portée, il n'y a rien à lire dans le NetworkStream car il a déjà été lu et désactivé. – Kevin
J'ai mis à jour ma question en fonction de votre commentaire apparemment correct. Ceci est fondamentalement problématique comme indiqué dans ma question. –
L'atténuation DOS est généralement effectuée au niveau de l'appliance réseau, où les données de flux peuvent être surveillées/analysées au fur et à mesure. Du point de vue de l'application, je ne suis pas certain que vous puissiez faire beaucoup pour détecter ou atténuer une telle attaque. Ma seule suggestion serait de lire le flux dans son intégralité dans un MemoryStream, de le tester, puis de le rejeter s'il ne l'est pas. Cela ne réduirait pas la charge de trafic TCP d'une attaque, mais pourrait empêcher le traitement/stockage inutile des données de la corbeille. – Kevin