Саммерфилд - Программирование на Python 3 (1077331), страница 110
Текст из файла (страница 110)
Словарь сз11 отображает пункты меню на соответствующие им функции. Модуль Сопзо1е — один из тех, что поставляются вместе с книгой; он содержит некоторые полезные функции получения значений, вводимых пользователем в консоли, такие как Сопзо1е, цес зс с!пц() и Сопзо1е. дес !псецег(), — они похожи на функции, разработанные в первых главах, и были объединены в модуль, чтобы их было проще использовать в разных программах.
Для удобства пользователей мы запоминаем последний введенный номерной знак, чтобы его можно было использовать в качестве значения по умолчанию, потому что выполнение большинства команд начинается с запроса номерного знака соответствующего автомобиля. Как только пользователь сделает свой выбор, вызывается соответствующая функция, которой передается номерной знак, и ожидается, что 492 Глава 1О. Сети каждая функция будет возвращать использовавшийся номер.
Так как цикл выполняется бесконечно, программа должна завершаться одной из функций; это будет показано ниже. бет Яет саг бета!1з(ргеч!ооз 1!свозе); 11оепзе, саг = гесгсече саг беса11з(ргеч1ооз 1соепзе) сг саг сз пос йопе: рг1пс("хсоепзе: (ОР,пзеасз: (![0]Р,пй!1еаре; (т[!])'тп" "Очпег: (![2])".Гогэас( 11оепзе, саг)) гетогп 1ссепзе Эта функция используется для получения информации о конкретном автомобиле. Поскольку большинству функций требуется запросить номер автомобиля у пользователя и часто необходимо получить некоторые сведения об автомобиле, мы вынесли эту функциональность в отдельную функцию геСгсече саг бетв11з() — она возвращает кортеж из двух элементов, содержащий номер автомобиля, введенный пользователем, и именованный кортеж Са гТор1е, хранящий количество посадочных мест, пробег и владельца (или предыдущий номер автомобиля и значение йопе, если был введен неопознанный номер).
Здесь функция просто выводит полученную информацию и возвращает использовавшийся номер автомобиля, который будет применяться в качестве значения по умолчанию при вызове следующей функции, которой потребуется этот номер. бет гетг1ече саг бетас1з(ргеч!ооз 11сепзе): 1ссепзе = Сопзо1е.реС зтг!пр("[1сепзе", "1!сепзе", ргеч!сиз 1!сепзе) ст пот 1ссепзе: гесогп ргеч1ооз 11сепзе, йопе 1ссепзе = 1ссепзе.оррег() ок, *баса = Папб1е гесоезС("ОЕТ САй ОЕТА1[5", 1ссепзе) 1Г пот ок; ргспт(бата[0]) гесогп ргечсопз 11сепзе, йопе гесогп 1ссепзе, СагТор1е(*баса) Это первая функция, реализующая взаимодействие по сети. Она вызывает функцию Лапб1е гесоезС(), которую мы рассмотрим немного ниже.
Функция принимает Лвпб1е гесоезС() любые данные, полученные в качестве аргументов, отправляет их серверу и возвращает все, что было получено от сервера. Функция Лап б1е гес без С ( ) ничего не знает об отправляемых и получаемых данных, она просто реализует сетевую службу. В приложении регистрации автомобилей, в соответствии с принятым протоколом запросов, первым аргументом всегда посылается имя команды, которая должна быть выполнена сервером, а в остальных аргументах — параметры команды, в данном случае это номер автомобиля. Согласно протоколу ответов сервер всегда возвращает кортеж, первым элементом которого является логический флаг, свидетельствующий Клиент ТСР 49э об успехе операции.
Если флаг имеет значение Ез1зе, то во втором элементе кортежа содержится текст сообщения об ошибке. Если флаг имеет значение Тгое, то кортеж будет состоять либо из двух элементов, где во втором элементе содержится текст подтверждения, либо из и элементов, где второй и все последующие элементы хранят запрошенные данные. Далее, если номер автомобиля не будет опознан, переменная ОИ получит значение Еа1зе, и тогда функция просто выведет текст сообщения об ошибке из бз!а(О), а вызывающей программе будет возвращен предыдущий номер автомобиля. В противном случае будет возвращен текущий номер (который теперь становится предыдущим номером) и кортеж Са гТор1е, созданный из списка баса (число мест, пробег, владелец).
ает алапде и!1еаде(ргеч!оаз 1таепве): 11сепве, саг = ге!мече саг бета!1з(ргечшаз 1!оепзе) 1Г оаг тз Иоле: гетагп ргеч1ооз 1!пеппе в!1еаде = Сопзо1е.де! !и!едет("И!1еаде", та!1еаде", сзг.зт1еаде, О) !Г и11еаде == О: ге!огп 11сепзе оН, *бата = Папа!е гепаезт("СНАИОЕ И!ЕЕАОЕ", 11оепзе, и!1еаде) 1Г по! одп рг!пт(бата(О]) е1зе: рг1гг("И11еаде зоооеззто11у олапдеб"] гетогп 1тоепзе Эта функция следует той же схеме, что и де! са г бе!а11з(), за исключением того, что после получения информации она обновляет один из элементов. Здесь фактически выполняется два сетевых запроса, поскольку функция ге!г1ече саг бе!а!1з() вызывает функцию Лзпб1е геОоезт() для получения информации об автомобиле. Здесь нам необходимо убедиться в корректности номера автомобиля и получить текущее значение пробега, которое будет использоваться как значение по умолчанию.
В данном случае ответ сервера всегда будет представлять собой кортеж из двух элементов, во втором элементе которого будет содержаться либо текст сообщения об ошибке, либо Иоле. Мы не будем рассматривать функцию спапде оипе г(), поскольку структурно она полностью повторяет функцию сзапде з!]езде(). Мы также не будем рассматривать функцию пеи гед!зтга!1Оп(), которая отличается лишь тем, что не пытается получить информацию об автомобиле вначале (так как вводится информация о новом автомобиле) и запрашивает у пользователя полный набор сведений, Для нас в этих функциях нет ничего нового и ничего, что было бы связано с программированием сетевых взаимодействий. бет Оо!!(*!драге): зуз.ехтт() 494 Глава 10.
Сети бе( втор вегчег(*1дпаге): Ьапб1е гедоевт("ВНОТООИИ", аа11 Гог гер1у=га1ве) вув,ех11() Если пользователь выбирает команду завершения программы, мы производим выход вызовом функции вув. ехСС(). Каждая функция, соответствующая некоторому пункту меню, получает в виде аргумента предыдущий номер автомобиля, но в данном случае в нем нет никакой необходимости. Мы не можем просто написать беу Со(С():, потому что в результате будет создана функция, не ожидающая аргументов, и при вызове такой функции с предыдущим номером автомобиля будет возбуждено исключение ТуреЕггог, свидетельствующее о том, что функция получила аргумент, который она не ожидала получить. Поэтому мы просто определили параметр *1дпоге, который способен принять любое число позиционных аргументов. Само имя Сдпоге ничего не значит для интерпретатора и используется исключительно для того, чтобы показать тому, кто будет сопровождать программу, что аргументы игнорируются функцией.
Если пользователь выбирает команду остановки сервера, мы с помощью функции Ьапб1е гедоевС() извещаем сервер и указываем при этом, что не ждем ответа. Сразу после отправки данных функция Ьапб1е ге- соевС() возвращает управление, не ожидая ответа, и мы завершаем работу программы вызовом функции вув, ех(С(). бег ьапб1е гесоевс( 1сеав, ча(с гог гер1у=тгое): Втвевтгост = втгосС.ВСгост("! 1") баСа = р1сК1е.боарв( 1теав, 3) Сгу; п(СЬ Восхетзападег(тор1е(пббгевв)) ав восх; восх,вепба11(ВсаеВСгост.расК(1еп(бата))) восх.вепба11(бата) 1Г пот аа)С Гог гер1у: гетогп в1ге баСа = восК.гесч(ВсаеВСгост.в1ае) в1ае = В!Севтгост.опрасх(в1ае батаКО) гево1С = Ьутеаггау() ч(П1е Тгое; баСа = восК.гесч(4000) 1( поС бата: ЬгеаК гево1С.ехСепб(бата) ст 1еп(гево1С) >= в1ге: Ьгеах гетогп рссх1е. 1оабв(гево1С) ехсерС восхет.еггог ав егг; рг1пс("(О): 1в сье вегчег гопп)пд?",Согаас(егг)) вув.ех1С(1) Клиент ТСР Эта функция реализует в программе все сетевые взаимодействия.
Она начинается с создания объекта втгьст. Зтгьст, который хранит одно целое число без знака с сетевым порядком следования байтов, после чего создается законсервированный объект, содержащий все полученные элементы данных. Функция ничего не знает и не делает никаких предположений об этих элементах данных. Обратите внимание, что здесь явно определяется протокол консервирования 3, чтобы гарантировать, что сервер и клиент будут использовать одну и ту же версию протокола, даже если на стороне сервера или на стороне клиента была обновлена версия интерпретатора Рус)топ.
Если бы нам было необходимо обеспечить возможность дальнейшего развития протокола обмена, мы могли бы предусмотреть передачу его номера версии (как мы делали это с двоичными форматами сохранения данных на диск). Это можно реализовать как на сетевом уровне, так и на уровне данных. На сетевом уровне мы могли бы предусмотреть передачу номера версии в виде второго целого числа без знака, то есть передавать длину блока данных и номер версии протокола. На уровне данных мы могли бы следовать соглашению, согласно которому законсервированный объект всегда является списком (или словарем), в котором первый элемент (или элемент с ключом ьуегв1опэ) содержит номер версии. (Вы получите шанс добавить версию протокола, когда будете решать упражнения.) Класс ЗЬЬКетМапаьег — это наш собственный менеджер контекста, представляющий собой сокет, готовый к использованию, — мы рассмотрим его чуть ниже.