| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Синхронизация приложений |
| Автор: former 9.9.2009, 22:27 |
| Что подразумевается под синхронностью. Предположим, что N компьютеров объединены в сеть через сервер. Требуется, что бы изменение объектов на форме любым пользователем мгновенно происходили у других пользователей. Каким образом это можно реализовать? С чего начать? Буду благодарен за ссылки. |
| Автор: Fedia 9.9.2009, 22:53 |
| Первое, что приходит на ум: использование http://delphiworld.narod.ru/base/sockets_in_delphi.html: TClientSocket & TServerSocket; Второе: сетевая СУБД, в которую записывать вносимые пользователем изменения. На таймере в каждом открытом приложении считывать эти изменения и отображать их на форме. Самый простой способ: общая сетевая папка, в ней файл в который записываются изменения. Опять же в таймере их считываем и отображаем на форме. Здесь нужно будет решать проблемы одновременного доступа к файлу. При работе с БД (не версионной типа MySql) такого быть не должно, поскольку там будет действовать принцип: кто последний тот и прав. |
| Автор: kami 9.9.2009, 22:55 |
Переделать любой из доступных исходников чата на TCP "под себя", где вместо пользовательских сообщений будет передаваться информация об изменениях объектов. Простейшие исходники чатов уже рассчитаны на рассылку "один ко всем", останется только придумать свой протокол обмена, чтобы все участники "чата" понимали, что от них хотят и что они должны сделать. Уточняющие вопросы: 1. Что подразумевается под объектами? 2. Один из пользователей изменил объект. В это же время (с запаздыванием на несколько микросекунд) этот же объект изменил другой пользователь. Если сеть локальная, то скорее всего положение будет следующим: - команда от первого пользователя отправляется на сервер. - команда от второго пользователя отправляется на сервер(для простоты будем именовать их №1 и №2). - №1 рассылается всем пользователям (в т.ч. и №2, возможно - за исключением №1), они меняют состояние своих объектов. - №2 отправляется всем пользователям (в т.ч. и №1, возможно - за исключением №2), состояние объектов опять меняется (это будет почти моментально) Результат: у пользователя 1 будет состояние от №2 (в принципе, как и положено - команда от 2 поступила чуть-чуть позднее) у пользователя 2 будет состояние от №1. (это в том случае, если поступающая команда не "дублируется" обратно). Имхо, это нужно будет предусмотреть как-то... |
| Автор: former 9.9.2009, 23:13 |
| Fedia, не подходит. kami 1. Технологическая схема, объектами которой являются задвижки, резервуары и т.д. Параметрами, которые необходимо передавать, являются физические величины и некоторые другие. Другими словами это параметры для модели тех. процесса. 2. Каждый пользователь отвечает за свой участок схемы. |
| Автор: kami 9.9.2009, 23:20 |
| former, Тогда - чат на TCP, вместо сообщений - управляющие команды (пускай даже и текстовые, чтобы меньше переделывать. Типа "Add ZObject XParam=3211 YParam=-12 ZParam=4311". Хоть я и не люблю передавать данные текстом) и всё. Проблемой будет непосредственно перевод действий в управляющие команды и обратно. Ну и отображение их, хотя это другая история. |
| Автор: kami 9.9.2009, 23:48 |
| former, давайте, все-таки пока отделим котлеты от гарнира (в смысле - мат.модель с ее расчетами от синхронизации приложений) В этом случае компонент-сервер чата стыкуется с мат.моделью просто "на ура": сервер чата знает о "черном ящике", что у него есть вход и назначает на себя его выход. При любом сообщении от клиента сервер чата передает данные в процедуру входа мат.модели, а когда та отработала - вызывается событие окончания расчетов. Обработанные данные черного ящика просто рассылаются всем клиентам. Что будет делать мат.модель в процедуре DoChanges - для сервера чата это маловолнующе. Ну разве что это будут очень долгие вычисления без выхода из этой процедуры, что, конечно, нежелательно, но - обходимо. |
| Автор: former 9.9.2009, 23:56 |
| kami, меня беспокоит не столько время на расчеты, сколько какой объем данных возможно передавать с помощью чата . Или это будет определяться производительностью сервера и возможностями сети. |
| Автор: kami 10.9.2009, 00:03 |
В идеале - неограниченный. В реальности - в зависимости от того, как написан чат (чат - это условно, просто судя по Вашей задаче, передача данных должна быть организована аналогично его структуре). Но, по всей вероятности - тоже неограниченный, ибо скорее всего ограничения по длине разово передаваемой информации будут заложены явно, что не составит труда для небольшого изменения) Добавлено через 1 минуту и 1 секунду Сейчас попробую написать работоспособный пример. |
| Автор: kami 10.9.2009, 01:05 |
нет, пример будет завтра (вернее, уже сегодня) вечером. Остановился на раздельной обработке передаваемых/принимаемых данных на сервере, еще немного+пара тестов и будет готово. |
| Автор: kami 10.9.2009, 12:38 |
| Сделал. Это адаптированные вырезки из более мощного проекта, поддерживающего приоритеты передачи данных, шифрование "на лету" BlowFish-ем и т.д. Комментариев нет, извините. Если что-то непонятно - спрашивайте. Тестирование проводилось на: - передачу данных в обе стороны - адекватное реагирование на разрыв соединения в режиме простоя - отсутствие утечек при всех действиях. Не проводилась проверка (лень, если честно) реакции компонентов на разрыв соединения непосредственно в процессе передачи данных, но думаю - ничего страшного не будет. Ограничение: Не стоит (но не значит, что нельзя) передавать данные в несколько сотен мегабайт от сервера клиентам - внутреннее хранилище данных основано на TMemoryStream, что при наличии десятков подключений приведет к задействованию памяти SourceStream.Size*ClientCount. В изначальных компонентах такого ограничения нет. Передача данных корреспонденту поддерживается несколькими методами (буфер, строка, TStream). Прием - только TStream. При необходимости - расширить на события с приемными буферами других типов несложно. У сервера есть методы "Передать всем" и "передать конкретному". Особенность: При передаче данных через TStream сетевой компонент становится его владельцем и САМ уничтожит его. Посему - передали Stream в метод и ЗАБЫЛИ про него. При приеме - наоборот. Получив TStream из сетевого компонента, владелец ОБЯЗАН его уничтожить. Пример работы с этими компонентами так же во вложении. P.S. Надеюсь, что гуру форума также не обойдут вниманием этот файл и подскажут, где в нем есть грабли и т.д. |
| Автор: former 10.9.2009, 14:47 |
| kami, спасибо за пример. Посмотреть теперь уже, к сожалению, смогу только в выходные. По результатам отпишу. |
| Автор: Akella 10.9.2009, 16:36 | ||
|
| Автор: former 10.9.2009, 17:04 |
| Akella, вроде бы я тебя никогда плюсами в репу не обижал. И kami не обижу, когда тема будет исчерпана. Просто некоторые после повышения в репутацию сваливают из темы и получается висяк. тише, а то нас THendle за флуд накажет |
| Автор: kami 10.9.2009, 17:37 | ||
Я бы сказал, что это не пример, а готовый шаблон для реализации Вашей задумки по
|
| Автор: former 13.9.2009, 17:07 |
| kami, спасибо (+)! Хороший шаблон! Если возникнут какие-нибудь вопросы, то напишу. Нашел еще вот http://www.devarticles.com/c/a/Delphi-Kylix/Sever-Side-Chat-Application-with-Borland-DelphiIndy/ несколько неплохих статей. Может ком-нибудь будет полезным. |
| Автор: kami 13.9.2009, 19:41 |
Да не за что. Давно уже собирался сделать что-то вроде выложенного здесь для простых задач сетевого обмена, но все руки не доходили. А тут такой случай. Для полного счастья (мне) осталось их чуть-чуть доработать, дописав пару свойств. Не знаю почему, но к Indy у меня стойкая аллергия, и чем выше версия - тем больше |
| Автор: former 13.9.2009, 20:36 | ||||
Думаю, что универсальности в задаче сетевого обмена добиваться нет смысла, т.к. многое определяется конкретными условиями и потребностями. С другой стороны, можно классифицировать типовые задачи и создать для них шаблоны.
О! Это уже интересно. |
| Автор: kami 13.9.2009, 22:59 |
Ну, я бы не был так категоричен Да ничего интересного. Главное при добавлении не дать доступа непосредственно к Server и Client сокетам (и их составляющим по возможности, особенно - касающихся приема и передачи). Пару свойств для идентификации соединений на server-side (к примеру, IP[ConnectionIndex:integer]:string) В общем - дело 20 минут. |
| Автор: former 14.9.2009, 22:36 |
| kami, откомпилированный вариант работает как часы. Возможно, что это связано с самим компилятором. Я компилирую в D2009. При компиляции предупреждения и сообщения об ошибках не появляются. Вот мой вариант компиляции. |
| Автор: kami 14.9.2009, 23:41 |
Надо было сразу говорить. У меня D7. Вроде, единственное, что нужно сделать - заменить везде String на AnsiString. Завтра гляну. |
| Автор: Romikgy 14.9.2009, 23:48 |
тогда уж на WideString... |
| Автор: former 14.9.2009, 23:53 | ||
Это не помогло. Замана PChar на PAnsiChar позволила передать данные, но ![]() |
| Автор: kami 15.9.2009, 07:30 |
Вот честно - не сталкивался с D2009, посему - могу только предполагать, но думаю, что не везде. В методах, взаимодействующих с пользователем - да, желательно бы на WideString. Но я так понимаю, что при этом нужно учитывать, что Length(myWideString)=2*Length(myAnsiString) при копировании из байтовых буферов, к примеру - TStream.Read? И кстати, какой условной директивой определяется, что это D2009 ? former, Ваш экзешник вообще ничего не выводит, хотя передача данных между клиентом и сервером идет |
| Автор: former 15.9.2009, 08:43 | ||
Если этот вопрос адресован мне. Лицензией.
Так я об этом и говорил. |
| Автор: kami 15.9.2009, 12:15 | ||
Не, я немного не про то. Я имел ввиду директивы условной компиляции типа
Чуствую, надо будет поставить на виртуалку 2009, потому что иначе много вопросов возникает... |
| Автор: bems 15.9.2009, 17:01 |
| это проверка версии компилятора. А тебе наверное нужно RTLVersion, или вообще SizeOf(Char) длина же в символах а не байтах. Так что тут умножение на 2 ни к чему |
| Автор: kami 15.9.2009, 19:03 | ||||
Я имел ввиду Потому что стандартный код для D7 (опуская проверки длины строки и размера потока)
с учетом будет выглядеть как-то так:
??? |
| Автор: former 15.9.2009, 20:00 |
| kami, все, нашел причину. В модулях компонента поменял String - AnsiString и Char - AnsiChar, а в главном нет. Вот в этом и была загвоздка. Моя вина. |
| Автор: kami 15.9.2009, 21:19 |
Тоже переделал, протестировал на D2009. Замена на Ansi коснулась только буферов в private секциях. Для упрощения использования в примере (при приеме данных) добавил функцию StreamToString |
| Автор: bems 15.9.2009, 21:43 | ||
ну да, верно. Если записывалось этим же кодом, скомпилированом в той же версии. |
| Автор: kami 15.9.2009, 21:59 |
Действительно. То есть и здесь нужно делать "соглашение о вызовах"... либо и там и там WideString либо AnsiString... Как все непросто с этим unicode... |
| Автор: bems 15.9.2009, 22:14 |
| имхо лучше везде записывать unicode, а код продублировать для разный делфей. Ну а там где встроенного нет - преобразовывыть руками. Зато никаких неопределенностей между модулями. |
| Автор: former 15.9.2009, 22:56 | ||
Это уже к вопросу о переносимости кода. А если таких сток много и под несколько версий нужно делать. ИМХО: лучше все же использовать |
| Автор: bems 15.9.2009, 23:05 |
| Ну и чем же лучше? Если используется это ваше соглашение (в начале строки есть информация о кодировке), то модуль скампилированый в ansi-версии должен уметь преобразоватьб для себя в ansi. Аналогично уникодный модуль - в уникод (и при этом возникает весьмя явная неопределенность с кодовой страницей, которую тоже нужно включать в соглашение). В то время как мой вариант (для уникодной версии использовать только встроенные механизмы, а для анси - преобразование) по сложности остаётся полностью "нутри" первого. Это значит что преобразования есть и там и там (только у меня они зависят только от версии дельфи) и нет потерь кодовой страницы. Добавлено через 3 минуты и 8 секунд Или (лучше всего) все машины в сети скомпилированы в одной версии. Если есть исключения - это их проблема и им нужно обновиться. И лучше бы этой версией была уникодная. |
| Автор: former 16.9.2009, 01:52 |
Само собой. Так оно и есть. Я имел виду при создании компонент (для примера). |