Глава 4:Сетевое программирование (часть1)
Когда я впервые прочел о разработке сетевых приложений, я был так напуган, что решил, будто это мне не по зубам. В итоге, когда один из заказчиков предложил написать программу для работы в сети, я не рискнул принять предложение. Увидев же парня, которого наняли для выполнения этой работы, я понял, что на самом деле мне бы это также удалось. Следовало бы послать ему благодарность за избавление от комплекса.
Однажды мне довелось прочесть, что за создание сетевого приложения разработчик должен требовать двойную или тройную плату. Должен сказать, на самом деле включение сетевых возможностей добавляет около 2% к стоимости проекта, кроме особо сложных случаев. Причина в том, что большая часть из того, что, как вам кажется, должно быть сделано, вовсе не нужна.
Команды и функции для сетевого программирования
Команды, которые используются при разработке сетевых приложений, приведены в таблице 4-1.
Наибольшее влияние на поведение FoxPro при попытке заблокировать файл или запись оказывает команда SET REPROCESS:
SET REPROCESS TO 0 возвращает системное сообщение [Попытка установить блокировку - нажмите ESCAPE для отмены], если первая попытка блокировки не удалась. Если система имеет процедуру обработки ошибок, то она имеет приоритет в обработке возвращаемого кода ошибки.
SET REPROCESS TO -1 бесконечный цикл попыток блокировки без выдачи сообщения, генерации ошибки или возможности отмены клавишей Esc.
SET REPROCESS TO -2 (или SET REPROCESS TO AUTOMATIC) повторяет попытки до успешной блокировки или отмены клавишей Esc.
SET REPROCESS TO expN (или SET REPROCESS TO <expN> SECONDS) команда в таком виде ограничивает число попыток блокировки значением expN или временным интервалом expN секунд.
Два подхода
При программировании сетевых приложений вы можете использовать два подхода:
1) Блокировка средствами FoxPro без использования FLOCK() или RLOCK(), предоставляя процедуре обработки ошибок разбираться с конфликтами; это традиционный подход.
2) Блокировка посредством команд FLOCK() или RLOCK(), установкой SET REPROCESS <expN> совместно с соответствующей логикой и обработкой ошибок процедурой, назначенной ON ERROR для определения способа разрешения конфликтов.
Первый подход самый простой и работает в 99.9% случаев, не внося дополнительных трудностей. Второй подход рекомендуется учебниками и требует более тщательного планирования.
Пять недопустимых слов
Ленни Брюс, ныне покойный комик, в свое время исполнял репризу о словах, которые не следует произносить на публике. Фактически он заработал немало денег произнося эти слова и периодически попадая за это в полицию в пятидесятые годы.
При работе в сети вы не можете сказать PACK. Команда PACK блокирует файл и не может быть использована, если в сети есть больше одного пользователя. Так что вы не можете использовать PACK.
Если нельзя использовать PACK, то также нельзя использовать DELETE. Вы можете спросить, как это, не использовать DELETE? Всем приходится удалять записи время от времени; это категорический императив. Если удаляете, то должны и упаковывать.
Вы не можете использовать REINDEX. Он блокирует файл. Вы не можете заблокировать файл если в системе есть другие пользователи. Может быть можно вывести сообщение:
"Упаковать? (все остальные пользователи должны выйти) <Да> <Нет>"
По-правде говоря, нельзя, потому что остальные пользователи могут не захотеть выйти из системы, по крайней мере, пока это не станет необходимым. Если программа написана правильно, такой необходимости обычно не возникает.
По той же причине вы не можете использовать ZAP. Как насчет маленьких хитростей с повторным использованием файла путем удаления всех его записей? Ну, если вы уверены, что он больше никому не нужен, это можно сделать. Если же есть вероятность, что данные в файле могут кому-нибудь понадобиться, его нельзя трогать. Из вышесказанного следует, что ваши приложения должны быть разработаны таким образом, чтобы над файлами, предназначенными для одновременного использования, не выполнялись команды REINDEX, ZAP, PACK.
Принудительная блокировка против автоматической
Решение в выборе техники. FoxPro поддерживает две функции принудительной блокировки: RLOCK() и LOCK() блокируют записи, FLOCK() блокирует файл. Синтаксис прост. Используйте их в составе любого выражения (например, IF или команды печати) или вызовите со знаком равенства (например, =RLOCK()). При успешной блокировке указанные функции возвращают .Т., в противном случае - .F.. Дальнейшие события определяются установкой SET REPROCESS и вашим кодом (например, IF NOT RLOCK() QUIT /ENDIF).
Кроме того, многие команды FoxPro автоматически пытаются заблокировать файл или запись. Если блокировка неудачна, возвращается код ошибки. Вы можете построить обработчик ошибок таким образом, чтобы он справлялся с этими проблемами. Если файл не может быть заблокирован, возвращается ошибка 108 или 1503. Для записи код ошибки - 109. Если вы попытаетесь открыть файл с монопольным доступом, а он уже занят другим пользователем, возвращается код ошибки 108. Ваша процедура обработки ошибок может определить дальнейший ход действий при получении соответствующего кода ошибки. Листинг 4-1 (Программа обработки ошибок многопользовательского приложения)
Если вы полностью положитесь на команды, автоматически блокирующие записи по мере необходимости (многие приложения построены именно по этому принципу), вам будет не нужно выполнять принудительную блокировку записей или файлов. Если все, что вы делаете - это ввод данных в список адресов с минимумом редактирования или вовсе без него, зачем беспокоиться о возможности претензий двух пользователей на одну и ту же запись в файле из 100000 строк. Вероятность такого конфликта практически равна нулю.
Реальная опасность редактирования записи без блокировки в том, что два пользователя могут менять содержимое записи в одно и то же время, и в результате пользователь, который первым сохранит данные, потеряет их после того как второй затрет их своей информацией. Все сказанное подразумевает, что вы не редактируете записи непосредственно - это ящик Пандоры в проектировании экранов ввода. Если вероятность возникновения конфликта мала, я не применяю принудительную блокировку записей.
Так отчего же столько шума вокруг всех этих принципов блокировки? Если вам не нужны функции FLOCK(), RLOCK(), LOCK(), не используйте их. Кроме того, вам они могут никогда не понадобиться при создании сетевых приложений. Не вводите себе в привычку использование PACK, ZAP и REINDEX. Есть другие более грамотные способы написания программ в FoxPro. Наибольшее влияние на цену разработки оказывают усилия, затраченные на проектирование программы.
Если у вас еще остались сомнения я буду говорить более конкретно. Если вы разрабатываете приложение так, как будто все самое худшее, что только может случиться, непременно произойдет, вы строите Роллс-Ройс для каждого из ваших заказчиков и берете с них цену Роллс-Ройса, тогда как все, что им нужно - это Фольксваген. Такой подход характерен для врачей, но не для нас.
Определение степени усилий
В большинстве систем основная часть базы данных состоит из нескольких ведущих файлов, обычно используемых для добавления данных или создания отчетов. Это значит, что вы вряд ли возвращаетесь в базу данных и меняете адрес Фреда Смита по 14 раз на дню. Вы вводите информацию о нем и раз в год что-нибудь в заведенной для него записи требует изменений. Он представляет интерес в основном для включения в отчет. Добавление записей или печать отчета не требует блокировки записей. Команда APPEND BLANK работает жутко медленно, и добавление нескольких сотен записей каждое утро с последующим повторным использованием ранее удаленных записей является наилучшим решением. Кроме того, я не рекомендую принудительную блокировку записей. Такой подход поднимает цену разработки, снижает скорость исполнения и обычно не нужен.
Если задача того требует (например, система заказов авиационных билетов, в которой каждое место в самолете представляет собой запись, а вероятность, что два агента будут требовать одну и ту же запись высока) обязательно выполняйте блокировку из программы. Однако, не думайте, что единственный способ летать это тот, что описан в учебнике. Парни, писавшие руководство, не должны были обосновывать ваш бюджет.