Сценарий, представленный в примере 12.4, действует подобно оригинальному серверу echo, но для обработки каждого нового соединения с клиентом ответвляет новый процесс. Так как функция handleClient выполняется в новом процессе, функция dispatcher может сразу продолжить выполнение своего главного цикла, чтобы обнаружить и обслужить новый поступивший запрос.
Пример 12.4. PP4E\Internet\Sockets\fork-server.py
дочерние процессы совместно используют дескрипторы родительских сокетов; прием ветвления процессов менее переносим, чем прием на основе потоков выполнения, — он не поддерживается в Windows, если не используется Cygwin или подобная ей оболочка;
import os, time, sys
from socket import * # получить конструктор сокетов и константы
myHost = » # компьютер-сервер, » означает локальный хост
myPort = 50007 # использовать незарезервированный номер порта
sockobj = socket(AF_INET, SOCK_STREAM) # создать объект сокета TCP sockobj.bind((myHost, myPort)) # связать с номером порта сервера
sockobj.listen(5) # не более 5 ожидающих запросов
def now(): # текущее время на сервере
return time.ctime(time.time())
activeChildren = []
def reapChildren(): # убрать завершившиеся дочерние процессы,
while activeChildren: # иначе может переполниться системная таблица
pid, stat = os.waitpid(0, os.WNOHANG) # не блокировать сервер, если if not pid: break # дочерний процесс не завершился
activeChildren.remove(pid)
def handleClient(connection): # дочерний процесс: ответить, выйти
time.sleep(5) # имитировать блокирующие действия
while True: # чтение, запись в сокет клиента
data = connection.recv(1024) # до получения признака eof, когда
if not data: break # сокет будет закрыт клиентом
reply = ‘Echo=>%s at %s’ % (data, now()) connection.send(reply.encode())
connection.close() os._exit(0)
def dispatcher(): # пока процесс работает
while True: # ждать запроса очередного клиента,
connection, address = sockobj.accept() # передать процессу
print(‘Server connected by’, address, end=’ ‘) # для обслуживания print(‘at’, now()) reapChildren() # теперь убрать завершившиеся потомки
childPid = os.fork() # копировать этот процесс
if childPid == 0: # в дочернем процессе: обслужить
handleClient(connection)
else: # иначе: ждать следующего запроса
activeChildren.append(childPid) # добавить в список # активных потомков
dispatcher()
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011