17688 / 12873 / 3366
Регистрация: 17.09.2011
Сообщений: 21,138
|
|
1 | |
Ваша реализация получения сообщений по определенному протоколу06.10.2012, 17:04. Показов 3379. Ответов 53
Метки нет (Все метки)
Третьего дня получил задание - реализовать клиентскую часть одного сетевого протокольчика.
Протокол простенький по своей спецификации, я бы даже сказал примитивный, но пара моментов реализации вкупе с несколькими условиями, которые были поставлены, создали довольно интересную задачку. Настолько интересную, что захотелось поделиться ей с общественностью. Спецификация: 1. Общение клиента с сервером происходит по протоколу TCP. 2. Общение инициирует клиент, первым отсылая сообщение. 3. Протокол асинхронный, то есть сервер может как отвечать на клиентские запросы, так и слать сообщения без предупреждения (уведомления о всяких событиях). Через это клиент должен постоянно находиться в режиме приема сообщений. 4. Сообщение - это строка незашифрованного текста в кодировке UTF-8. 5. Сообщения разделяются символом \n 6. Символ \r игнорируется. 7. Сообщения состоят из параметров, разделенных пробелами. 8. Количество параметров неограничено, но их всегда как минимум два. 9. Длина каждого параметра неограничена*. Так же параметр может быть пустой строкой (два подряд идущих пробела, например). 10. Параметр может состоять из любых символов, имеющихся в таблице юникода. 11. Если в тексте параметра встречается конструкция вида {n}, где n - целое число в промежутке [0; int.MaxValue], то следующие n байтов текста после закрывающей квадратной скобки считываются как есть и являются частью этого параметра, то есть любые специальные символы не интерпретируются (напр. пробелы и переносы строки). * Основная масса сообщений - довольно короткие, в районе 100 символов. Но, отослав определенный запрос, можно получить и портянку текста длиной в несколько мегабайт. Задача: реализовать клиентскую часть, отвечающую за прием сообщений и генерирующую для каждого полученного сообщения массив строк, где каждый элемент массива является параметром сообщения. Ну и, конечно же, чтобы жизнь медом не казалась, имеется несколько обязательных к выполнению условий: 1. Максимально возможная эффективность обработки сообщений, т.к. обработчиков будет создано много (может быть в районе тысячи). 2. Вследствие первого пункта у обработчика должен быть минимальный футпринт в памяти, то есть в ходе работы объекты в куче стоит плодить только при крайней необходимости. К примеру, вполне разумно один раз создать буфер для приема данных, но весьма неразумно - создавать отдельный буфер для каждого сообщения. Если, конечно, ваше решение вообще будет использовать буферы (моё использует). Обработчик будет частью другой, довольно сложной системы, потому должен быть максимально легковесным. 3. Вследствие все того же первого пункта обязательно использование асинхронной модели для приема сообщений, т.к. тысячу потоков вам никто не предоставит. Вроде бы ничего не забыл. Если кому-то нечего делать - попытайтесь реализовать. Решение выкладывать не обязательно - это не просьба о помощи (решение уже есть). Но очень интересно было бы услышать, с какой стороны вы бы подошли к проблеме.
0
|
06.10.2012, 17:04 | |
Ответы с готовыми решениями:
53
Необходимо разработать программу для получения почтовых сообщений по протоколу POP3 Соединение по определенному протоколу Передача сообщений по протоколу UDP Настройка системы для отправки сообщений по протоколу SMTP |
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|
05.09.2017, 19:27 | 41 |
А какого примерно размера?
То есть это вариант с list.Add() ?Переиспользование списка? Хм... Очень интересно. Вы рассказали о своей реализации. И она совсем не такая, как у меня (то есть я бы делал по-другому).
0
|
192 / 199 / 82
Регистрация: 11.04.2013
Сообщений: 1,086
|
|
05.09.2017, 19:40 | 42 |
а зачем нужен List<string> если есть StringBuilder?
As others have pointed out, I would probably use StringBuilder. The List may have to resize many times; the new implementation of StringBuilder does not have to resize.
0
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|
05.09.2017, 19:46 | 43 |
List<string> - полагаю, чтобы сформировать string[] .StringBuilder - чтобы сформировать строку.
0
|
192 / 199 / 82
Регистрация: 11.04.2013
Сообщений: 1,086
|
||||||
05.09.2017, 19:51 | 44 | |||||
Я к тому, если ввести какой нибудь служебный символ и просплитить не быстрее будет?
0
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|
05.09.2017, 19:59 | 45 |
Конечно же это будет работать.
Тут дело в оптимальности и минимальном мемори футпринте.
0
|
192 / 199 / 82
Регистрация: 11.04.2013
Сообщений: 1,086
|
|
05.09.2017, 20:02 | 46 |
0
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
||||||
05.09.2017, 20:07 | 47 | |||||
EVG-1980, этот код:
0
|
192 / 199 / 82
Регистрация: 11.04.2013
Сообщений: 1,086
|
|
05.09.2017, 20:12 | 48 |
0
|
17688 / 12873 / 3366
Регистрация: 17.09.2011
Сообщений: 21,138
|
|
05.09.2017, 22:43 [ТС] | 49 |
256 символов, если память не изменяет.
Ага. Ага, после того как полное сообщение готово — вызов Clear. В список складываются полностью считанные поля пока не будет готово полное сообщение. В StringBuilder накапливается поле, которое считывается в данный момент. Нет, коробочный сплит не очень эффективный — у него сложность O(3n), если я не ошибаюсь. Плюс лишний мусор. Если уж сравнивать, то просто List, т.к. StringBuilder будет использоваться в обоих случаях и "выносится за скобки". Список удобнее потому что не создает в памяти одну большую строку, которая все равно потом выбросится. Лишний мусор.
0
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|
12.09.2017, 19:51 | 50 |
Правильно ли я понял:
Вы отправили серверу сообщение с конструкцией {int.MaxValue} (но само это сообщение, естественно, не было такого размера), сервер распарсил эту конструкцию и начал "стакать" все последующие сообщения от других клиентов, как одно гигантское поле?
0
|
17688 / 12873 / 3366
Регистрация: 17.09.2011
Сообщений: 21,138
|
|
12.09.2017, 22:06 [ТС] | 51 |
Ага.
Нет, размер передаваемых данных соответствовал заявленному. То есть 2 гига по сети ушло. Нет, он съел эти 2 ГБ для хранения моего сообщения. Если отсылать 2 параллельно с разных соединений, то съест 4 ГБ.
0
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|
13.09.2017, 09:06 | 52 |
kolorotur, похоже, я всё напутал...
Сервер ведь на своём "дежурном" сокете прослушивает входящие соединения от клиентов и для каждого такого соединения создаёт "отдельный" сокет. То есть никакого "стака" данных от разных клиентов в принципе не может быть. Это ведь для клиента существует реализация своеобразного "события" от сервера, в котором может прийти "портянка" из нескольких сообщений от сервера в виде одного соединения. То есть вы ради "кспиримента" отправили серверу 2 гига данных?! И сколько времени у вас это заняло? Час?! Рискну предположить, что данные, которые вы отправили на сервак состояли преимущественно из латиницы. Если сервер работает с юникодными строками, то не понятно, почему он съел всего 2 гига памяти. Должно быть как минимум в 2 раза больше, ведь один байт латиницы в UTF-8 будет соответствовать двум байтам в UTF-16. И если сервер для декодирования сообщения использует "накопление" поля в промежуточных структурах, то "мгновенный" рабочий набор такого обработчика должен быть не менее 8 гигов! Четыре из них - для накопления поля, четыре - займёт результирующая строка, которую это поле представляет. Или я где-то не прав?
1
|
17688 / 12873 / 3366
Регистрация: 17.09.2011
Сообщений: 21,138
|
|
13.09.2017, 10:56 [ТС] | 53 |
Ну конечно — обычное TCP-соединение.
Соединение открыто постоянно и сообщения летают взад-назад. Можно, конечно, открыть соединение, отправить сообщение, получить ответ и закрыть сокет. В некоторых случаях так и делается, но парсер писался для общего случая. Ну да, а в чем проблема? Несколько секунд — у меня копия сервера на локалке стоит для разработки и тестирования Правильное предположение. Цель теста была послать большое сообщение, а не проверить возможность сервака обрабатывать юникод (он это умеет) Сервер не на дотнете написан, что он там со строками делает — понятия не имею. Возможно, он сообщения хранит в UTF-8 в памяти или вообще как просто байты и интерпретирует по надобности. Отправил 2 гига двухбайтового текста, потребляемая память доросла до 2ГБ, потом буквально на секунду резко скакнула до 4ГБ и обратно до 2. Мутит он что-то Если на дотнете с его 16-битными символами, то да. Но по факту дорастает до 2, потом резко прыгает до 4 и снова падает до 2.
0
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|
13.09.2017, 11:16 | 54 |
Возможно, этот двухкратный скачок потребления памяти связан с тем,
что сервер сначала накапливает поле в чанках (как это делает "новый" StringBuilder в C# (с .NET 4.0)),потом выделяет строку и копирует в неё содержимое чанков, а затем резилит и сами чанки. Наверное, сервер на плюсах и использует ansi строки.
1
|
13.09.2017, 11:16 | |
13.09.2017, 11:16 | |
Помогаю со студенческими работами здесь
54
Отправка сообщений на почту по протоколу SMTP без использования .NET-функций Ваша идея, моя реализация Моё творчество - ваша реализация Нужны примеры реализации передачи сообщений по tcp протоколу, только суть, ничего лишнего Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |