Утечка памяти строки чтения StreamReader

#c# #memory #tcpclient #tcplistener

#c# #память #tcpclient #tcplistener

Вопрос:

Я создаю сервер, и я заметил, что у него немного странная утечка памяти. На моем сервере я ограничил общее количество подключений, которые может сделать один IP-адрес, до 8. Это работает отлично.

Однако, когда я использую LOIC для DoS моего сервера, я замечаю, что, хотя он создает только 8 экземпляров сетевого класса, метод ReadLine StreamReader занимает большую часть памяти.

Изображение VS Analyse

Я проверил с помощью консоли.WriteLine, что данные фактически не принимаются, он просто ожидает этого метода. И память увеличивается и заполняет все мои 16 ГБ.

Просто чтобы отметить, существует только 8 экземпляров этого класса с одного IP-адреса, и поскольку я использую Dos’ing, а не DDoS’ing, он всегда поступает только с моего IP-адреса (я проверил это).

В чем причина этой проблемы и как я могу ее исправить?

РЕДАКТИРОВАТЬ:
соответствующий код:

         string data;
        Dictionary<string, string> variables = null;
        try {

            while (_listen) {

                data = null;

                while ((data = _reader.ReadLine()) != null) {
 

Комментарии:

1. Если вы не отправите новую строку ( rn ), строка будет продолжать увеличиваться

2. @LucasTrzesniewski Я не выбираю, что отправляется. Это DoS-атака, которую я запускаю против своего сервера.

3. Я знаю, это было только объяснение того, почему вещь потребляет память. Я подразумевал, что вам не следует использовать ReadLine , если вы заботитесь о DoS-атаках.

4. А, хорошо, спасибо. Что бы вы порекомендовали?

5. Я напишу ответ.

Ответ №1:

ReadLine Функция будет буферизовать данные в строку, пока не встретит a rn ( n самостоятельно этого не сделает). StringBuilder Для этого используется a . Итак, если полученные вами данные не содержат последовательности rn , в конечном итоге будет выделено много памяти для одной строки.

Здесь у вас есть одно простое решение: вы должны ограничить максимальную длину строки, которую ваш сервер готов читать. Это необходимо для работы с вредоносными данными — никогда не доверяйте получаемым данным, если ваш сервер может стать жертвой DoS-атак.

Итак, решение, как TextReader метод расширения:

 public static String ReadLineSafe(this TextReader reader, int maxLength)
{ 
    var sb = new StringBuilder();
    while (true) {
        int ch = reader.Read();
        if (ch == -1) break; 
        if (ch == 'r' || ch == 'n')
        { 
            if (ch == 'r' amp;amp; reader.Peek() == 'n') reader.Read(); 
            return sb.ToString();
        } 
        sb.Append((char)ch);

        // Safety net here
        if (sb.Length > maxLength)
            throw new InvalidOperationException("Line is too long");
    }
    if (sb.Length > 0) return sb.ToString();
    return null; 
}
 

Это исходный ReadLine код с добавленной проверкой безопасности.

О, и вы должны обернуть свой NetworkStream в BufferedStream для чтения netowrk, чтобы избежать вызова recv каждого байта.

Комментарии:

1. Просто быстрый вопрос. Что делать, если длина получаемых данных непредсказуема. Допустим, у меня есть пользовательский тип в описании, если бы я указал 512 в качестве максимальной длины, прочитал бы он все 2048 байт данных?

2. В зависимости от вашего варианта использования вам придется подумать о том, какой разумный верхний предел для данных, которые вы ожидаете получить. Например, пользователь вряд ли введет в поле текст размером 1 МБ. Затем добавьте некоторый запас прочности и используйте это. Но ограничения, которые вы должны установить, зависят от конкретного приложения. Приведенная выше функция не будет считывать больше maxLength символов.

3. Хорошо, спасибо. Я буду использовать этот код. Я отмечу это как ответ, если он будет работать нормально.

4. Я попробовал этот метод, и вместо того, чтобы просто замедлять работу моего компьютера и заполнять память, он фактически выводит его из строя. Может ли это быть вызвано этим методом?

5. Игнорируйте мой предыдущий комментарий, это была ошибка в моем коде, а не в вашем. Отлично работает, это исправило утечку памяти. Спасибо