Последовательность вызовов методов сокетов в клиентских программах вроде той, что показана в примере 12.2, имеет более простой вид, — фактически, половину сценария занимает логика подготовительных операций. Главное, о чем нужно помнить, — это то, что клиент и сервер при открытии своих сокетов должны указывать один и тот же номер порта, и дополнительно клиент должен идентифицировать компьютер, на котором выполняется сервер. В наших сценариях сервер и клиент договорились использовать для связи порт с номером 50007, вне диапазона стандартных протоколов. Ниже приводится последовательность вызова методов сокета на стороне клиента:
sockobj = socket(AF_INET, SOCK_STREAM)
Создает в клиентской программе объект сокета, так же как на сервере.
sockobj.connect((serverHost, serverPort))
Открывает соединение с компьютером и портом, где программа сервера ждет запросы на соединение от клиентов. В этом месте клиент указывает строку с именем службы, с которой ему необходимо связаться. Клиент может передать имя удаленного компьютера в виде доменного имени (например, starship.python.net) или числового IP-адреса. Можно также определить имя сервера как localhost (или использовать эквивалентный ему IP-адрес 127.0.0.1), указав тем самым, что программа сервера выполняется на том же компьютере, что и клиент. Это удобно для отладки серверов без подключения к Сети. И снова номер порта клиента должен в точности соответствовать номеру порта сервера. Обратите еще раз внимание на вложенные скобки — так же, как в вызове метода bind на сервере, мы передаем методу connect адрес хоста/порта сервера в виде кортежа.
Установив соединение с сервером, клиент попадает в цикл, посылая сообщения построчно и выводя то, что возвращает сервер после передачи каждой строки:
sockobj.send(line)
Пересылает серверу очередную строку байтов сообщения через сокет. Обратите внимание, что список сообщений по умолчанию содержит строки байтов (b‘…’). Так же, как и на стороне сервера, данные, передаваемые через сокет, должны иметь вид строк байтов, впрочем, при необходимости это может быть результат кодирования текста вручную вызовом метода str.encode или результат преобразования с помощью модуля pickle или struct. Когда строки для передачи передаются в аргументах командной строки, они должны быть преобразованы из типа str в тип bytes — эта операция выполняется клиентом с помощью выражения-генератора (тот же эффект можно было бы получить вызовом функции map(str.encode, sys.argv[2:])).
data = sockobj.recv(1024)
Читает очередную строку ответа, переданную программой-сервером. Технически этот вызов читает до 1024 байтов очередного ответа, которые возвращаются как строка байтов.
sockobj.close()
Закрывает соединение с сервером, посылая сигнал «конец файла».
Вот и все. Сервер обменивается одной или несколькими строками текста с каждым подключившимся клиентом. Операционная система обеспечивает поиск удаленных компьютеров, направляя пересылаемые между программами байты через Интернет и (с помощью TCP) обеспечивая доставку сообщений в целости. При этом может выполняться еще масса работы — по пути наши строки могут путешествовать по всему свету, переходя из телефонных линий в спутниковые каналы и так далее. Но при программировании на языке Python мы можем оставаться в счастливом неведении относительно того, что происходит ниже уровня вызовов сокетов.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011