Russian Qt Forum
Май 21, 2024, 15:17 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Коллективное написание многопоточного сервера на Qt  (Прочитано 22341 раз)
merke
Гость
« : Март 19, 2010, 14:26 »

Здравствуйте, дорогие форумчане!
Среди нас есть как начинающие программисты так и гуру. Но все кто либо писавший на любом языке программирования рано или поздно встречается с работой с сетью. В большинстве случаев требуется написать клиент/серверные приложения. В интернете существует большинство исходных кодов для других сред программирования в которых реализуются многопоточные сервера. Я уже много раз сталкивался  с задачей написания подобных приложений на Qt, но в меру моей не профессиональности программирования в данной среде, у меня не получалось реализовать рабочий многопоточный сервер. Рыл учебники, русскоязычные форумы, зарубежные, но ни где не мог найти простую, доступную реализацию сервера.
Я хочу предложить вам  написать общими усилиями многопоточный сервер на Qt. Проделанная нами общая работа, очень поможет в будущем программистам, которые будут сталкиваться с подобной задачей. Они смогут получить готовые классы, которые будут интегрировать в свои проекты.
Предлагаю всем кто заинтересован в данном проекте, составить техническое задание, собрать всю необходимую информацию, обсудить все возможные варианты реализации.
Предполагаю, что у кого то уже есть подобная реализация, пожалуйста поделитесь исходными кодами. Мы Вам будем очень благодарны!!!

С уважением, Александр!
Записан
ieroglif
Гость
« Ответ #1 : Март 19, 2010, 20:39 »

перепиши GNE - все тебе будут мега благодарны =)
Записан
SABROG
Гость
« Ответ #2 : Март 20, 2010, 00:17 »

Предлагаю всем кто заинтересован в данном проекте, составить техническое задание, собрать всю необходимую информацию, обсудить все возможные варианты реализации.

Тогда нужно сразу договориться. Никаких boost::asio и сторонних сетевых библиотек. Реализация полностью на Qt.
Добиться решения 10k проблемы на Qt не выйдет из-за того, что в отличае от asio и других библиотек Qt не использует Completion Port в Windows. Кто может сравнить реализацию под linux, использует ли Qt оптимальную работу с сокетами в этой ОС или тоже иная реализация?

Думаю будет правильным включить QAbstractSocket::LowDelayOption, чтобы отрубить Nagle алгоритм.

Без потоковый вариант (асинхронный, но single threaded) думаю можно не рассматривать. Нам нужна закадровая работа сервера, которая не будет прерывать основной поток и блокировать главный цикл событий. Значит нужно на основании каких-то эмпирически вычесленных параметров выбрать максимальное количество потоков в приложении, между которыми будет распределяться вся работа с сокетами. Вариант "один поток - один клиент" - утопия, его не рассматриваем.

Техническое задание:
- реализовать ограничение по скорости как на прием, так и на отправку (можно сделать через ограничение размера буффера сокета)
- реализация подобия firewall'a, чтобы добавить возможность отвергать соединение с IP адресами и/или MAC адресами. Пример можно взять в IRC, где можно банить конкретный IP, целиком подсеть или шлюз.

Если будем писать протокол, то:
- использование QDataStream для приема/передачи команд
- реализация сжатия для передаваемых пакетов
- реализация ping-pong команд для определения задержек (latency) и жив ли наш клиент/сервер на той стороне. Не является ли соединение - зомби.
- реализация шифрования пакетов
- квотирование по скорости, времени, переданных и/или присланных объемов данных (в случае использования сервера в качестве файлообменника)
- система авторизации (в этом случае без БД не обойтись)
- использование QtConcurrent для распараллеливания долгих операций (собственные, более быстрые, варианты алгоритмов сжатия и шифрования)
- для передачи файлов реализация p2p алгоритма, хеширование, докачка. Чтобы пользователи могли выкачивать файлы даже тогда, когда сервер не доступен, по аналогии с torrent, directconnect.
- реализация системы обновлений клиента (патчи/апдейты и тому подобное) (прим: у протокола должна быть версия, чтобы старые клиенты не могли общаться с сервером)
- изучение возможности присвоить клиентам уникальные ID, чтобы можно было их идентифицировать не только по IP или логину, но и компьютеру на котором работает клиент. Привязка к компьютеру, пусть даже с защитой от дурака и без использования ОС зависимого API, но чтобы было не очевидно.
- реализация CAPTCHA для отсеивания ботов
- реализация "ловушки" в ответ на неверную команду от клиента (telnet, бот и т.п.) Сервер передает сообщение клиенту "не подключаться в течении n времени". Если в течении этого времени клиент подключается, то он банится на определенный промежуток времени. Позволит отсекать "блуждающих" по портам ботов, которые могут пытаться определить протокол и реализовать попытку взлома.
- использование UDP протокола для "необязательных" операций. Поиск файлов, попытка быстрого подключения для проверки чего-либо.
---
Пока всё. Нужно найти способ сделать стресстест для такого сервера. Прикинуться smtp серваком например и разместить на форумах фиктивный e-mail адрес с доменным именем Улыбающийся Через некоторое время стабильно начнет сыпаться спам раз в секунду или быстрее. Можно заделаться под DC клиент и отвечать на все поисковые запросы, особенно TTH, лавина соединений хлынет очень быстро. Думаю уговаривать людей потестировать сервачок ни к чему не приведет.
Записан
hackoff
Гость
« Ответ #3 : Март 20, 2010, 06:14 »

Вариант "один поток - один клиент" - утопия, его не рассматриваем.
Расскажите пожалуйста, чем плох этот вариант?
Записан
merke
Гость
« Ответ #4 : Март 20, 2010, 07:54 »

SABROG, огромное спасибо что откликнулись, вы прекрасно поставили задачу!
Теперь давайте порассуждаем.
Отключение алгоритма Нагла может существенно уменьшить скорость. Суть алгоритма (вообще то RTFM) для непосвящённых - если Ваше приложение отправляет данные по 1 байту и делает это часто, алгоритм Нагла постарается собирать данные в более крупные сегменты. Отключение его приведет к тому, что стек будет пытаться отослать данные немедленно, что приведет к весьма нерациональному использованию канала - на 1 байт данных будет приходиться около 40 байт служебных заголовков.

Пожалуйста, SABROG, объясните почему не следует применять метод "один поток - один клиент"?

Насчёт протокола:
Цитировать
использование QDataStream для приема/передачи команд
Как я помню при передаче данных с использованием QDataStream в поток в начало дописывается 4 байта служебной информации. Поэтому клиент например написанный на Visual Basic или в другой среде уже не сможет корректно общаться с сервером. Это конечно же извращение писать клиента на vb, но всё же, не так давно я занимался такой порнографией, только сервер был на vb, а клиенты на кутях.

Вы описали столько дополнительных возможностей, которые можно прикрутить к проекту, это здорово. Но сейчас чтобы не заморачиваться всем тем, надо для начала реализовать саму голую, продуманную, стабильную многопоточность. Сервер будет писаться из расчётов, что количество одновременно соединённых клиентов будет >1000. Тут в помощь придёт большой опыт наших специалистов на форуме, я очень надеюсь, что они нам помогут найти рациональное решение реализации столь загруженного сервера, который будет в меру своей объёмности по вычислительным процессам, не таким ресурсоёмким.

Пожалуйста, дорогие форумчане, присоединяйтесь к проекту! Перспектива колоссальная! Если мы реализуем такой проект, который описал нам SABROG, это будет огромнейший вклад!

И так, на повестке дня, все у кого есть идеи, может готовые решения реализации многопоточного сервера различными методами, выкладывайте, объясняйте почему именно так вы предлагаете реализовать многопоточность!

Огромное спасибо всем кто уже присоединился к проекту!

Записан
garryHotDog
Гость
« Ответ #5 : Март 20, 2010, 12:07 »

Всем привет!
Код:
"один поток - один клиент"
- отбрасывайте сразу, моя Вам совет!!!! Писал(да и щас дописываю его) такое приложение, изначальный вариант был 1 клиент - 1 поток, потом столкнулся с проблемой что потоки жрут слишком много виртуальной памяти! Попробуй запусти 1000 потоков и пусть у всех в run стоит msleep(1000) (эмитация соединения) - посмотрите сколько памяти сожрется!!!!!!

    Реализовал приложение на асинхронных сокетах, но есть свои небольшие непонятки....а так идея хорошо насчет совместной разработки...только кто будет координатором и под какую сеть(задачу) будет сервак?Непонимающий
Записан
SABROG
Гость
« Ответ #6 : Март 20, 2010, 13:42 »

Отключение алгоритма Нагла может существенно уменьшить скорость. Суть алгоритма (вообще то RTFM) для непосвящённых - если Ваше приложение отправляет данные по 1 байту и делает это часто, алгоритм Нагла постарается собирать данные в более крупные сегменты. Отключение его приведет к тому, что стек будет пытаться отослать данные немедленно, что приведет к весьма нерациональному использованию канала - на 1 байт данных будет приходиться около 40 байт служебных заголовков.
Если рассматривать общение по протоколу Ethernet как серию мелких пакетов (меньше или равное MTU - 1500 байт - 40 байт заголовка), то у сервера есть аж 1460 байт для отсылки команды. Если в таком пакете вместе с опкодом команды передаются данные, которые умещаются в 1460 байт, то между каждой командой будут задержки. Первая может возникнуть, если общий объем данных не дотягивает до полноразмерного пакета. 1459 байт, вместо 1460. Алгоритм Нагла будет ждать еще данных от сервера. Тут мне не понятна такая вещь, если пакет от сервера самый первый, то фраза в большинстве статей "будет ждать ответа ACK от клиента (200 ms delay) перед посылкой следующего пакета" приводит в замешательство. Если предыдущего пакета небыло, то что, deadlock? Понятно, что если длину данных невозможно разбить без остатка на MSS - 1460 байт, то каждый раз мы будем иметь задержку из-за Нагла на последних пакетах.

В общем как ни крути, а применение этого алгоритма целесообразно только в случае экономии траффика, так как при отключенном Нагле для отправки одного байта TCP пакет наполняется "воздухом" до максимального размера MSS. В локальных сетях в нем совершенно нет никакого смысла.

Вообще хотелось бы слышать лично ваше мнение, вместо копирования чужого с форума : http://forum.sources.ru/index.php?showtopic=184504 Тем более, что чуть ниже человеку дают недвусмысленно понять, что он не прав. Отключение алгоритма Нагла в большинстве случаев не приводит к снижению скорости передачи, а наоборот.

Пожалуйста, SABROG, объясните почему не следует применять метод "один поток - один клиент"?
У тебя оперативной памяти не хватит на 1000 клиентов + создание и уничтожение потоков дополнительная нагрузка на сервер.

Насчёт протокола:
Цитировать
использование QDataStream для приема/передачи команд
Как я помню при передаче данных с использованием QDataStream в поток в начало дописывается 4 байта служебной информации. Поэтому клиент например написанный на Visual Basic или в другой среде уже не сможет корректно общаться с сервером. Это конечно же извращение писать клиента на vb, но всё же, не так давно я занимался такой порнографией, только сервер был на vb, а клиенты на кутях.
Хмм, о клиентах не на Qt я не подумал. Но если отказаться от QDataStream, то мы потеряем переносимось между платформами, он все-таки Byte-Order независим. Может быть проще будет вытащить эти "магические" значения из исходников Qt?

---
Общение между потоками сервера можно реализовать через систему сигналов и слотов. Несмотря на кажущуюся потерю в скорости у этой системы есть несомненные плюсы. При использование мутексов, callback'ов или паттерна типа Observer вызывающий всегда блокируется до завершения вызываюемой функции. В случае двух потоков это приведет к остановке одного, пока другой будет что-то обрабатывать. По сути это ничем не будет отличаться от однопоточной системы. Нам это не нужно. Благодаря системе событий Qt мы можем отправить сигнал и не дожидаясь ответа продолжить обрабатывать других клиентов. Ответ, как результат работы слота, будет приходить тоже как сигнал. Например клиент запрашивает файл. В данный момент мы находимся в одном из 5 потоков, каждый поток обрабатывает 1000/5 = 200 клиентов. Представим, что наш сервер это Макдональдс. Клиенты стоят в 5 очередях, которые обслуживает 5 кассиров. Каждый кассир принимает заказ и передает его на выполнение другим сотрудникам, повору например. При этом мы видим, что кассир не дождавшись выполнения заказа уже берет заказ у следующего клиента. У нас будет также, 5 потоков обрабатывают команды клиентов и передают на выполнение одному или двум потокам, которые выполняют некую долгую блокирующую операцию. Так вот клиент запрашивает файл. Сначала мы спрашиваем у остальных клиентов, есть ли у них уже этот файл и если есть, то просим их подключиться к запрашиваемому клиенту для передачи кусков, иначе сами передаем ему список таких клиентов, если он за фаерволом. Если у них такого файла нет, то передаем имя файла в другой поток, который читает его с устройства, кеширует некий объем данных, чтобы минимизировать количество обращений к диску, так как эта операция всегда долгая. И по готовности передает сигнал. Получив этот сигнал мы зависываем порцию данных. Сигнал может содержать информацию о том сколько данных готово для пересылки, какому клиенту они предназначаются. Стоит ли ожидать еще данных. С целью разгрузки сервера можно заставить все подключенные клиенты скачать каждому свой диапозон данных файла, который запрашивал другой клиент. После пересылки дать им команду подключиться к запрашиваемому и отдать свой кусок. При этом если в будущем кто-то запросит тот же файл, то большинство клиентов уже будут иметь какую-то его часть и серверу даже не будет необходимости его передавать заново. Система хорошо будет работать на новинках. Значит должен быть счетчик популярности для каждого файла, чтобы можно было принять однозначное решение о распределении кусков файлов по клиентам. С целью минимизации траффика между клиентом и сервером было бы рационально заставить два и более клиентов общаться между друг другом напрямую минуя сервер. Значит нужно каждому подключившемуся клиенту сообщать о клиентах, которые уже подключены к серверу, а им в свою очередь сообщить о новом подключившемся или отключившемся. В идеале хотелось бы организовать децентрализованную систему, где любой из участников сети мог бы взять на себя роль сервера, таким образом, чтобы при отказе в работе основного сервера другой смог бы занять его место. Также хотелось бы создать интеллектуальную систему, которая смогла бы вычислять самый быстрый маршрут между клиентом и сервером. Таким образом, чтобы другие клиенты могли выступать в роли proxy серверов. Зачастую в том же DirectConnect другие компьютеры из пиринга не доступны для скачивания файлов, однако часто есть промежуточные клиенты, которые видят сразу обе подсети и могут наладить связь между клиентами из разных сетей. Но это уже идеи касающиеся конкретно p2p технологий.
« Последнее редактирование: Март 20, 2010, 14:22 от SABROG » Записан
merke
Гость
« Ответ #7 : Март 20, 2010, 13:56 »

garryHotDog, Добро пожаловать! Улыбающийся
Спасибо за все разъяснения по поводу почему лучше не использовать метод "один поток - один клиент"!

Да, изменить исходники класса QDataStream не составит труда. Насчёт переносимости я совершенно согласен.

Я хотел бы быть координатором, но не настолько хорошо знаю Qt. Нужен программист, который уже занимался командным написанием проектов через интернет. Потому что здесь нужно будет чётко распределять задачи между программистами и согласовывать работу. Предлагаю на роль КООРДИНАТОРА - SABROG. Вы согласны?

Давайте все вместе определимся под какую задачу будет писаться сервер! Может напишем сетевую игру, например Монополия?! Или есть какие либо другие интересные предложения?


« Последнее редактирование: Март 20, 2010, 14:00 от merke » Записан
SABROG
Гость
« Ответ #8 : Март 20, 2010, 14:59 »

Давайте все вместе определимся под какую задачу будет писаться сервер!

Меня интересует идея написания клиента и сервера в одном лице. Хотелось бы построить децентрализованную анонимную файлообменную сеть, с зашифрованным протоколом. Распределить ответственность за распространение файлов равномерно по каждому участнику такой сети. Клиенты будут выкачивать "не нужные" им куски популярных файлов (в пределах настроек естественно, не больше, но и не меньше определенного объема), чтобы потом были тысячи альтернативных источников. Пользователь такой программы никогда не будет на 100% уверенным какие части и каких файлов он передал другим. Я знаю, что таких проектов несколько, но ни один из них не удовлетворяет полностью.
В торренте хорошо то, что ты можешь обсудить раздачу, посмотреть постеры, скриншоты, дополнительное описание, отзывы. Поэтому правильно было бы написать протокол, который позволит делать все тоже самое, но в децентрализованной сети, где трекера не будет как такового. В этом мог бы помочь движок WebKit'a. Предположим, что у меня есть фильм, я оформил раздачу. Дижок клиента создан так, что ты не можешь знать какой именно клиент хранит этот фильм. Ответ может приходить как от временного сервера, так и через "псевдо" прокси клиентов. То есть все клиенты видят общий список фильмов, но владельцев не видят, так как информация о них просто не приходит, а не скрывается. Если я хочу скачать раздачу, то вместе с фильмом я скачиваю дополнительную, служебную информацию, о раздаче. Таким образом, если оригинальный владелец пропадет из сети, то любой клиент, который качал этот фильм имеет на своем компьютере оформленную раздачу с постерами, описанием, скриншотами и обсуждением. Таким образом, на каждом клиенте хранится не вся база "форума", а лишь та часть, которая ему была интересна. При добавлении нового комментария к фильму информация об обсуждении обновляется у всех скачавших этот фильм. Если человек ничего не качал, а только принимает участие в обсуждении, то информация обновляется лишь у тех у кого есть этот фильм. Если в данный момент нет ни одного клиента с раздачей, то её нет и в списке "топиков", обсуждение невозможно до тех пор пока не появится хотябы один источник. Если после добавления комментария к фильму заходит еще один источник, то ему пересылаются все новые изменения, которые произошли в момент его отсутствия с раздачами, которые он имеет на своем компьютере. Синхронизация короче.

В данном случае речь идет о пиратском контенте. В связи с "оживлением" правительства в отношении пресечения пиратов эта тема сейчас наиболее остро стоит. Я сторонник такой идеологии "Послушал музыку - не понравилась, покупать не обязан. Посмотрел фильм - не понравился, покупать не обязан. Поиграл в игру - не понравилась, покупать не обязан". Сколько люди затратили денег на создание фильма это только их проблемы. Людям, которым фильм не понравился должны возвращать деньги в кинотеатрах. Если ты создаешь продукт, то все риски возлагаются только на тебя. Если я потрачу огромную сумму, чтобы нанять вертолет, дорогих актеров, особнях на мальдивах, а потом сниму треш, то деньги, которые я "отобью" у кинотеатров по сути будут ворованными. Хотябы взять тот же сайт IMDB, много ли фильмов оценок ниже 3 из 10? Докучи! Это значит, что примерно 2/3 людей вышли из кинотеатра или остались недовольными покупкой DVD от просмотра фильма, а значит отдали свои деньги ДАРОМ ни за что.
Записан
garryHotDog
Гость
« Ответ #9 : Март 20, 2010, 19:19 »

Я как понимаю ты предлагаешь написать не только клент\сервер, но и новый протокол вообще?! Идея мощьная просто она может оказаться не востребованной....не просто так ЕСТЬ и ИСПОЛЬЗУЮТСЯ P2P(различные вариации протокола), торрент и т.д....изобретать новый велосипед??? есть ли смысл???

Я как понимаю тема топика была - разработка приложения на QT !? Предлагаю, написать клент\сервер под уже готовую сеть например Gnutella 2.0 или Gnutella 0.6....это уменьшит время разработки самого протокола....это лично мое мнение....ла и еще - а зачем шифрование для файло обменной сети?Непонимающий это только замедлит работу клиента\сервера!
Записан
SABROG
Гость
« Ответ #10 : Март 21, 2010, 01:10 »

Да смысла нет конечно писать с нуля. Был вроде клиент Qtella, да в 2004 году удачно сдох. А сейчас в gnutella у всех одна Shareaza стоит, а она на MFC написана. Грустно.

Может бота напишем какого (не обязательно для p2p)? Подмигивающий
Записан
garryHotDog
Гость
« Ответ #11 : Март 21, 2010, 01:41 »

вот Бот уже интересней...только нужен "умный" бот)))) нужна только сеть.....ща по гуглю что нибудь
Записан
IMPOMEZIA
Гость
« Ответ #12 : Март 21, 2010, 10:04 »

Цитата: SABROG
Кто может сравнить реализацию под linux, использует ли Qt оптимальную работу с сокетами в этой ОС или тоже иная реализация?
Используется не эффективный poll вместо epoll.
Записан
mcrads
Гость
« Ответ #13 : Март 21, 2010, 11:06 »

немного технической информации из практики (с чем сталкивался)... пардон если повторяю чьи-то идеи...
необходимо четко представлять себе механизмы работы задуманного. Так, например, я писал виртуальный файловый сервер и клиент к нему. Казалось бы что может быть проще? распараллелить подключения на потоки, все красиво и аккуратно. В результате я взял 10 подключений на поток. гиг оперативы выдержал около 150 клиентов. Но при этом файловая система могла выполнять единовременно только одну операцию. стал распараллеливать и ее. в результате производительная упала еще сильнее. Пришел к выводу что наиболее оптимальным решением будет литая ФС, асинхронная работа клиентов и лимит на длину пакета - чтобы успевались обслужиться и другие клиенты. Собстренно грубо это выглядит так: один клиент отправляет на 300 байт, другой 150. мы обслужили 100 байт первого, потом сто второго, еще сто первого, оставшиеся второго и оставшиеся первого. все в порядке очередности - кто постучался. Такой механизм не тратит лишние ресурсы на потоки и зависит только от производительности системы.

Однако если вы ИЗНАЧАЛЬНО задумали параллельную обработку - сервер можно смело делать многопоточным.
Учтите, что многопоточная обработка общих данных не всегда гарантирует 100% результат. (что такое мутексы я знаю, к тому же их использование сведет на нет распараллеливание обработки общих данных)

Хотя возможно я немного неправильно решал задачу - буду благодарен за поправки и замечания.

Систему команд я реализовывал путем записи в QByteArray определенной последовательности байт для каждой комманды. например первые 10 байт - команды, 3-5 байт - разделитель, остальное - данные, аргументы и тд.

Поправьте если что..
« Последнее редактирование: Март 21, 2010, 20:03 от Sickfar » Записан
garryHotDog
Гость
« Ответ #14 : Март 21, 2010, 19:46 »

для SABROG:

 Если не сложно разъясни мне пожалуйста следующее:

 Как я понял алогритм Нагла это в своем роде буферизация на сокете, делается для того что бы не нагружать канал маленькими пакета, типа данные 1 байта (а служебка будет 20байт заголовок IP +tcp заголовок+ 14 байт заголовок Ethernet), так?Непонимающий

  Если да, то что нам дает функция сокета flush() - по ее описанию - отправляем имеющиеся данные на сокете,в обход алгоритма Нагла что ли?Непонимающий

 и как отключить алгоритм нагла в QT сокетах???
« Последнее редактирование: Март 21, 2010, 20:29 от garryHotDog » Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.081 секунд. Запросов: 20.