Запустим этот сервер локально и посмотрим, как он работает (клиент и сервер могут запускаться на разных компьютерах, как в предыдущих примерах). Сначала предположим, что этот сервер уже был запущен на локальном компьютере в одном окне, и запустим несколько клиентов, которые попробуют пообщаться с ним. В следующем листинге приводится диалог в двух клиентских окнах консолей в Windows. В первом окне просто дважды был запущен сценарий echo—client, подключающийся к серверу, а во втором запускался сценарий testecho, запускающий восемь программ echo—client, выполняющихся параллельно.
Как и ранее, сервер просто возвращает любой текст, отправленный клиентом, однако здесь не выполняется приостановка с помощью функции time.sleep. Заметьте, что во втором окне клиента в действительности выполняется сценарий с именем echo—client-50008, который соединяется с сокетом второго порта на сервере, — это тот же самый сценарий echoclient, но в нем жестко определен другой номер порта (к сожалению, первоначальный сценарий не позволяет передавать ему номер порта):
[окно клиента 1]
C:\…\PP4E\Internet\Sockets> python echo-client.py
Client received: b"Echo=>b’Hello network world’ at Sun Apr 25 14:51:21 2010"
C:\…\PP4E\Internet\Sockets> python echo-client.py
Client received: b"Echo=>b’Hello network world’ at Sun Apr 25 14:51:27 2010"
[окно клиента 2]
C:\…\PP4E\Internet\Sockets> python echo-client-5008.py localhost Sir Galahad
Client received: b"Echo=>b’Sir’ at Sun Apr 25 14:51:22 2010"
Client received: b"Echo=>b’Galahad’ at Sun Apr 25 14:51:22 2010"
C:\…\PP4E\Internet\Sockets> python testecho.py
В следующем листинге приводится вывод в окне, где был запущен сервер. Первые три сообщения о соединении соответствуют запущенным клиентам echo—client; остальные — результат взаимодействия с восемью программами, порожденными сценарием testecho во втором клиентском окне. Этот сервер можно также запустить в Windows, потому что вызов select доступен на этой платформе. Сопоставьте эти результаты с программным кодом в сценарии сервера, чтобы понять, как он действует.
Обратите внимание, что для сценария testecho подключение новых клиентов и ввод данных мультиплексируются вместе. Если внимательно изучить вывод, можно заметить, что эти операции перекрываются во времени, потому что все они управляются единственным циклом событий на сервере. На практике вывод сервера наверняка каждый раз будет выглядеть по-разному. Сообщения о подключении и обслуживании клиентов будут перемешиваться почти случайным образом из-за различных временных задержек на разных компьютерах. Тот же эффект можно наблюдать в серверах, поддерживающих ветвление и многопоточную модель выполнения, но в них переключение между циклом диспетчера и функциями обслуживания клиентов выполняется автоматически самой операционной системой.
Заметьте также, что когда клиент закрывает сокет, сервер получает пустую строку. Мы следим за тем, чтобы сразу же закрывать и удалять такие сокеты, иначе они будут без нужды снова и снова попадать в список, просматриваемый вызовом select, в каждой итерации главного цикла:
[окно сервера]
C:\…\PP4E\Internet\Sockets> python select-server.py select-server loop starting
Connect: (‘127.0.0.1’, 59080) 21339352
got b’Hello network world’ on 21339352
got b» on 21339352
Connect: (‘127.0.0.1’, 59081) 21338128
got b’Sir’ on 21338128
got b’Galahad’ on 21338128
got b» on 21338128
Connect: (‘127.0.0.1’, 59082) 21339352
got b’Hello network world’ on 21339352
got b» on 21339352
[результаты testecho]
Connect: (‘127.0.0.1’, 59083) 21338128
got b’Hello network world’ on 21338128
got b» on 21338128
Connect: (‘127.0.0.1’, 59084) 21339352
got b’Hello network world’ on 21339352
got b» on 21339352
Connect: (‘127.0.0.1’, 59085) 21338128
got b’Hello network world’ on 21338128
got b» on 21338128
Connect: (‘127.0.0.1’, 59086) 21339352
got b’Hello network world’ on 21339352
got b» on 21339352
Connect: (‘127.0.0.1’, 59087) 21338128
got b’Hello network world’ on 21338128
got b» on 21338128
Connect: (‘127.0.0.1’, 59088) 21339352
Connect: (‘127.0.0.1’, 59089) 21338128
got b’Hello network world’ on 21339352
got b’Hello network world’ on 21338128
Connect: (‘127.0.0.1’, 59090) 21338056
got b» on 21339352
got b» on 21338128
got b’Hello network world’ on 21338056
got b» on 21338056
Помимо большей подробности этого вывода есть еще одно тонкое, но важное отличие, на которое следует обратить внимание: в данной реализации было бы неразумным вызывать функцию time.sleep для имитации выполнения продолжительной операции — так как все клиенты обрабатываются в одном и том же цикле, задержка остановит их все, и расстроит весь смысл мультиплексирующего сервера. Напомню, что серверы, выполняющие мультиплексирование вручную, как в данном примере, лучше всего работают, когда обслуживание клиентов занимает минимальный промежуток времени, в противном случае необходимо предусматривать специальные способы обслуживания.
Прежде чем двинуться дальше, необходимо сделать еще несколько замечаний:
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011