|
Вопрос # 3 050/ вопрос открыт / |
|
Здравствуйте, эксперты!
Есть сервер на Indy TCPIdServer и несколько клиентов на TIdTCPClient. Клиенты подключаются к серверу и отправляют комманды. Дупустим, один клиент отправил комманду добавления записи в БД на сервере. Понятно что на клиенте операция записи/отправки сообщения с коммандой серверу и потом сразу операция чтения/приема сообщения с результатом от сервера. А как отослать другим клиентам???
Нужно:
1) Cтрочки кода как отправить простенькое сообщение "Hello" всем клиентам, подключенным к серверу (Какой это список и как к нему обращаться? Защищен ли этот список в плане параллельного обращения к его элементам?), просто рассылка без учета что там делает клиент.
2) Если клиенты разнородные, например один авторизовался как продавец, а другой - как менеджер, то одному нужно отсылать только его записи, а менеджеру - записи всех клиентов. В случае добавления каким-то клиентом записи как на сервере определить какой клиент кем является и чтобы понять, отсылать ли ему информацию о добавленной записи?
3) Как должен быть устроен клиент чтобы принять сообщение от сервера с новой записью, которую внес другой клиент? Ну то есть он же должен все время как-то прослушивать приходящие сообщения помимо отправки своих и чтения ответа, но при этом компонент клиентский. Как это организовать? Может на клиенте в отдельном потоке поставить постоянное чтение из сети сообщений, а отправлять без ожидания ответа следующей коммандой (асинхронно)?
 |
Вопрос задал: Roman Novikov (статус: Посетитель)
Вопрос отправлен: 30 июля 2009, 15:07
Состояние вопроса: открыт, ответов: 1.
|
Ответ #1. Отвечает эксперт: Вадим К
Здравствуйте, Roman Novikov!
Вы смотрели в демках инди пример чата (chat)? Там половина Ваших вопросов решается и описана.
вопрос номер два). а как думаете, для чего придумали имя/пароль? Клиент логиниться, сервер по базе данных проверяет, смотрит права и объязаности клиента.
вопрос номер три)
можно сделать например так. Клиент время от времени далает запрос "нет ли новых сообщений". И всё, никаких проблем. Сразу решается проблема, что сообщение может прийти тогда, когда его не ждут. Единственное - придётся на сервере очередь делать.
 |
Ответ отправил: Вадим К (статус: Академик)
Время отправки: 30 июля 2009, 15:24
Оценка за ответ: 5
|
Мини-форум вопроса
Всего сообщений: 17; последнее сообщение — 8 августа 2009, 02:55; участников в обсуждении: 2.
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 16:55 [#1]:
2) Суть вопроса другая. Интересуют не полномочия клиентов, а механизм рассылки информации именно тем кому по этим полномочиям предназначена. То есть приходит сообщение от какого-то клиента.. что дальше делать? Пробегаться по всему списку подключений на самом компоненте, брать конктерное подключение, сверять его айпи с айпи в базе у клиентов выуживая его статус(манагер/продавец) и потом отправлять в случае удовлетворенности статусом (или наоборот сверять отмеченные онлайн-айпишники с айпи в подключениях)? Или же создавать свой список в котором хранится информация о подключенных клиентах (ссылка) с их статусами и прочей нужной инфой) чтоб не обращаться к БД?
Сам принцип нормальный мне нужно растолковать.
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 17:03 [#2]:
3) Вариант не удобный. На клиенте обновления будут происходить не в реальном времени, можут же и простые сообщения посылаться для общения продавцов и менеджеров. Если увеличивать частоту опроса то в случае более крупной системы с большим сислом пользователей нагрузка на сервер большая. И хранить всю очередь изменений для всех пользователей тяжеловато.
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 17:04 [#3]:
1) Помню что-то смотрел, пример старый и не устроил.
|
|
Вадим К (статус: Академик), 30 июля 2009, 17:08 [#4]:
а никто по базе не бегает. каждый клиент обслуживается в своем потоке. на каждого клиента на сервере есть свой "серверный объект", который и хранит всю информацию и никаких проблем.
если хочеться более реальных обновлений - тогда клиент должен всё время находиться в режиме "жду данных от сервера".
Можно также использовать UDP. по отдельному порту делается рассылка с помощью UDP, что данные обновились, а потом клиенты сами вычитывают.
"Помню что-то смотрел, пример старый и не устроил." тогда рекомендую забросить эту задачу. Вы её не решите.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 17:29 [#5]:
Ну так понятно что каждый клиент обрабатывается параллельно в своем потоке. Вот нужно после прихода сообщения от одного клиента проникнуть в другие объекты и отправить сообщение другим клиентам, а объект не хранит статус.
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 17:30 [#6]:
>тогда клиент должен всё время находиться в режиме "жду данных от сервера".
Вот механизм этого режима мне интересен.
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 17:33 [#7]:
>Вы её не решите.
для этого и обращаюсь за помощью, собственно цель проекта.
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 17:37 [#8]:
>Можно также использовать UDP. по отдельному порту делается рассылка с помощью UDP, что данные обновились, а потом клиенты сами вычитывают.
Помнится мне Вы как-то отвергали вариант установки дополнительно на клиентском приложении серверного компонента...
|
|
Вадим К (статус: Академик), 30 июля 2009, 17:47 [#9]:
">Вы её не решите.
для этого и обращаюсь за помощью, собственно цель проекта."
уточню. никто не поможет тоже.
">Можно также использовать UDP. по отдельному порту делается рассылка с помощью UDP, что данные обновились, а потом клиенты сами вычитывают.
Помнится мне Вы как-то отвергали вариант установки дополнительно на клиентском приложении серверного компонента..."
а где я написал о сервере? никакого дополнительного сервера. UDP рассылает сервер.
">тогда клиент должен всё время находиться в режиме "жду данных от сервера".
Вот механизм этого режима мне интересен."
Обычный read с сокета с большим таймаутом. кончился таймаут - снова читаем.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 21:21 [#10]:
>а где я написал о сервере? никакого дополнительного сервера. UDP рассылает сервер.
Речь тогда была о том что с сервеорного приложения надо было отправить сообщение клиентскому приложению, но ставить на клиентское приложение серверный компонент не стоит.
а как жеж иначе клиентское приложение примет UDP-пакеты?
|
|
Roman Novikov (статус: Посетитель), 30 июля 2009, 21:25 [#11]:
>Обычный read с сокета с большим таймаутом. кончился таймаут - снова читаем.
Вот! Об этом я и писал выше, что клиент должен все время читать сокет в отдельном потоке. Из основного потока отправили комманду и забыли, а в читающем потоке вернется ответ.
|
|
Вадим К (статус: Академик), 31 июля 2009, 03:04 [#12]:
"а как жеж иначе клиентское приложение примет UDP-пакеты? " вот так и примет. в UDP нет клиента - сервера
"Из основного потока отправили комманду и забыли, а в читающем потоке вернется ответ. " а вот так лучше не делать без синхронизации. А то будут проблемы. и ещё какие. Писать/читать в/с сокет(а) должен в один момент только один поток. При этом надо обеспечить себе целостность данных. Ведь если в один и тот же момент два потока захотят читать или писать, то будет каша. А вот если один только читает, а другой пишет - то будет нормально.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 4 августа 2009, 10:57 [#13]:
>"а как жеж иначе клиентское приложение примет UDP-пакеты? " вот так и примет. в UDP нет клиента - сервера
То есть мне с клиента отправлять запросы и в той же функции считывать ответ, а для внеплановых сообщений с сервера использовать UDP-пакеты, от сообщений в которых (например, уведомление о новой записи в БД) клиент примет решение запросить ли данные по TCP? На клиенте нужно будет поставить UDP-сервер с тем же портом.
Я правильно понял вашу мысль?
>а вот так лучше не делать без синхронизации.
Предполагается что основной поток пишет в сокет, а дополнительный только читает. Ну и критическую секцию использовать конечно.
Только мысль у меня пока еще не дозрела что и как лучше. - подскажите на что обратить внимание. А если второстепенный поток работает и в нем стоит чтение с сокета, его когда можно приостановить? - то есть если данные еще не поступали, но в потоке стоит чтение и в данный момент я вызову приостановку потока, после этого я сразу смогу писать в сокет, а потом возобновить читающий поток? И интересно как себя поведет в случае если приостановка вызвана в момент когда считываются данные.
|
|
Вадим К (статус: Академик), 4 августа 2009, 11:25 [#14]:
Пишу большими буквами "В UDP НЕТ ПОНЯТИЯ СЕРВЕР - КЛИЕНТ". Это всё фантазии инди. Но даже если и так, на клеинте сервер не нужен. Сервер, только сервер рассылает уведомления.
Вторая половина сообщения - набор мыслей.
Разнести на два потока - это хорошо, но я бы не советовал этого делать.
Что бы понять, что такое сокет, представляйте себе его в виде 2 труб. Одна на чтение, другая на запись. Но трубы не безлимитные. Если при записи записать слишком много данных, то остальные не продвинешь.
Лучше, что бы на уровне пересылки данных была целостность. То есть, клиент отрпавил пакет, сервер принял, потом наоборот.
А если клиент в этот момент принимает пакет, значит сервер должен отправлять. Но тогда если хочеться ещё и отправлять, то сервер либо должен иметь два потока на сокет (очень простой способ сделать себе гемморой), либо должен использовать неблокирующие сокеты.
Но вопрос в другом. Зачем плодить всё это, если можно работать без него? Вы что думаете, что пакеты будут только и бегать между клиентом и сервером и сами пакеты будут по десятку мегабайт?
И вопрос на последок. А хоть какой то код уже есть? или это всё пока ещё боязнь?
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Roman Novikov (статус: Посетитель), 7 августа 2009, 22:53 [#15]:
Сейчас одна основная проблема с сокетами: как клиенту получить информацию об операциях других клиентов. Если обрабатывать запрос однго клиента то все понятно и просто: клиент отправил, сервер считал, выполнил запрос к БД и выслал результат.
То есть надо обеспечить 2 основны задачи:
1) клинет послал запрос и ждет ответ -> сервер обработал входящее сообщение и отослал уведомление о статусе запроса -> клент считал результат.
2) сервер в результате сообщения одного из клиентов совершил добавление записи -> сервер всем клиентам отсылает сообщение с идентификатором записи -> [каждый клиент считывает сообщение с информацией] -> клиент отправляет сообщени с запросом на получение записи с полученным идентификатором.
С первым пунктом все ясно разобрались. А вот со вторым еще не до конца. - В квадратные скобки я выделил тот участок, который мне пока не ясно как реализовать. Тут либо на клинте должно как-то стоять чтение в цикле из сокета tcp-пакетов, которое приостанавливается на время в моменты когда клиент сам делает запросы с последующим чтением ответа в той же функции. - Этоа идея мне более близка, но пока не пригодна в том виде в каком я вижу рализацию (хотелось бы подстказки). Либо вариант с UDP-пакетами, который отправляет сервер, а клиент получает и делает стандартный запрос (например пришло сообщение что добавлена запись с таким-то id и клиент отправляет запрос на порлучение её). - этот вариант наверное сейчас более проще сделать без гемороя, но по вашим словая я в растерянности что же нужно сделать на клиенте чтобы проxитать udp-пакет и обработать.
|
|
Roman Novikov (статус: Посетитель), 7 августа 2009, 23:09 [#16]:
Не знаю как точно поведет себя программа в следующей ситуации (думаю что не прокатит):
Допустим сделали thread на клиенте, который все время читает из сокета, то есть запущен поток и он на сторочке Readln() ждет поступления данных . Когда клиент по комманде должен отправить сообщение серверу, то он вызывает приостановку thread (suspend/resume кажется), отправляет запрос, получает ответ и снова запускает thread.
Вот асинхронный режим, когда как 2 трубы, клиент в одну отправляет и забывает, а из другой читает все время... вроде прозрачен. Но какие тут ньюансы? То есть все время писать и много нельзя? (тут вы имели ввиду что сервер не справится как с ddos?)
|
|
Вадим К (статус: Академик), 8 августа 2009, 02:55 [#17]:
А зачем такие извращения? когда тред вызывает read, он отмечается в ядре как "ожидающий операций ввода/вывода". И это блокирующая ситуация. И врядли его получиться перевести в режим сна (а даже если и получиться.... хотеть читать он от этого не перехочет ).
В целом технология интересная, но я боюсь, что Вы её испоганили.
Вы хотите создать суперсистему, но поставили слишком большой барьер для себя. И не перепрыгните. Потом будете жаловаться на меня, что я "путал следы", "не хотел рассказывать" и так дальше.
Поэтому пойдем по более простому пути. Чтение/запись только в одном треде. Тред либо занимается передачей данных, либо ждет данных. Но для этого не обязательно применять read. Есть более простой способ сказать системе, что мы ждем данных. Это функция select. Но только в windows надо применять другую WSAEventSelect. Она может ждать изменения состояния сокета и "события". Получается так. тред вызывает эту функцию и "висит на ней". Выход с нее будет в трех случаях.
1) таймаут. тут и так всё понятно. Можно послать серверу keep-alive пакет, что бы подтвердить, что мы живы.
2) событие на сокете. Например "есть данные для чтения". Читаем и обрабатываем.
3) "событие" - это главный поток решил что то записать и подготовил буфер с данными.
На русском о этой функции можно почитать тут http://www.realcoding.net/article/view/1833 , а на английском - конечно тут http://msdn.microsoft.com/en-us/library/ms741576(VS.85).aspx
Сокеты придется кодировать вручную (можно конечно и инди прикрутить), но результат может превзойти все ожидания.
Попутно замечу, что если тред "висит" на select'e, то он практически не потребляет процессорных ресурсов. Но как только появляться данные, ось разбудит тред и даст возможность прочитать.
Галочка "подтверждения прочтения" - вселенское зло.
|
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|