Вспомогательный модуль перенаправления потоков ввода-вывода

vspomogatelnyj modul perenapravleniya potokov vvoda vyvoda Сетевые сценарии

Для иллюстрации действия метода makefile в примере 12.10 приводится реализация различных схем перенаправления потоков ввода-вывода вызывающей программы в сокеты, которые могут использоваться для взаимодействий с другим процессом. Первая из этих функций перенаправляет стандартный поток вывода, и именно ее мы использовали в главе 10; другие функции перенаправляют стандартный поток ввода и оба потока, ввода и вывода, тремя различными способами.

Объект обертки, возвращаемый методом socket.makefile, по своей природе допускает возможность прямого использования файловых методов read и write, и независимо от стандартных потоков ввода-вывода. Представленный пример также использует эти методы, хотя и неявно, через встроенные функции print и input доступа к потокам ввода вывода, и отражает типичные способы использования данного инструмента.

Пример 12.10. PP4E\Internet\Sockets\socket_stream_redirect.py

############################################################################ Инструменты для подключения стандартных потоков ввода-вывода программ без ГИ к сокетам, которые программы с графическими интерфейсами (и другие) могут использовать для взаимодействий с программами без ГИ.

############################################################################

import sys

from socket import *

port = 50008 # передать другой порт, если этот

#   уже занят другой службой

host = localhost# передать другое имя хоста для подключения

#   к удаленным слушателям

def initListenerSocket(port=port):

инициализирует подключенный сокет для вызывающих сценариев, которые играют роль сервера sock = socket(AF_INET, SOCK_STREAM)

sock.bind((», port)) # слушать порт с этим номером

sock.listen(5) # длина очереди ожидающих запросов

conn, addr = sock.accept() # ждать подключения клиента return conn # вернуть подключенный сокет

def redirectOut(port=port, host=host):

подключает стандартный поток вывода вызывающей программы к сокету для графического интерфейса, уже ожидающего запуск вызывающей программы, иначе попытка соединения потерпит неудачу перед вызовом метода accept sock = socket(AF_INET, SOCK_STREAM)

sock.connect((host, port)) # вызывающий сценарий действует как клиент file = sock.makefile(‘w‘) # интерфейс файла: текстовый режим, буфериз.

sys.stdout = file # обеспечить вызов sock.send при выводе

return sock # на случай, если вызывающему сценарию

# потребуется работать с сокетом напрямую

def redirectIn(port=port, host=host):

подключает стандартный поток ввода вызывающей программы к сокету для получения данных из графического интерфейса sock = socket(AF_INET, SOCK_STREAM)

sock.connect((host, port))

file = sock.makefile(‘r’) # обертка с интерфейсом файла

sys.stdin = file # обеспечить вызов sock.recv при вводе

return sock # возвращаемое значение можно игнорировать

def redirectBothAsClient(port=port, host=host):

подключает стандартные потоки ввода и вывода вызывающей программы к одному и тому же сокету;

в этом режиме вызывающая программа играет роль клиента: отправляет сообщение и получает ответ sock = socket(AF_INET, SOCK_STREAM)

sock.connect((host, port)) # открыть в режиме ‘rw’

ofile = sock.makefile(‘w’) # интерфейс файла: текстовый режим, буфериз. ifile = sock.makefile(‘r’) # два объекта файла, обертывающих один сокет sys.stdout = ofile # обеспечить вызов sock.send при выводе

sys.stdin = ifile # обеспечить вызов sock.recv при вводе

return sock

def redirectBothAsServer(port=port, host=host):

подключает стандартные потоки ввода и вывода вызывающей программы к одному и тому же сокету;

в этом режиме вызывающая программа играет роль сервера: получает сообщение и отправляет ответ sock = socket(AF_INET, SOCK_STREAM) sock.bind((host, port)) # вызывающий сценарий — сервер sock.listen(5)

conn, addr = sock.accept()

ofile = conn.makefile(‘w‘) # обертка с интерфейсом файла

ifile = conn.makefile(‘r‘) # два объекта файла, обертывающих один сокет sys.stdout = ofile # обеспечить вызов sock.send при выводе

sys.stdin = ifile # обеспечить вызов sock.recv при вводе

return conn

Чтобы протестировать этот сценарий, в примере 12.11 определяется пять групп функций, реализующих клиентов и серверы. Функции-клиенты выполняются в этом же процессе, а функции-серверы запускаются в отдельных процессах переносимым способом с помощью модуля multiprocessing, с которым мы познакомились в главе 5. Таким образом, функции клиентов и серверов выполняются в различных процессах, но общаются между собой посредством сокета, подключенного к стандартным потокам ввода-вывода внутри процесса тестового сценария.

Пример 12.11. PP4E\Internet\Sockets\test-socket_stream_redirect.py

############################################################################ тестирование режимов socket_stream_redirection.py

############################################################################

import sys, os, multiprocessing

from socket_stream_redirect import *

############################################################################ # перенаправление вывода в клиенте

############################################################################

def server1():

mypid = os.getpid()

conn = initListenerSocket() # блокируется до подключения клиента

file = conn.makefile(‘r’)

for i in range(3): # читать вывод клиента

data = file.readline().rstrip() # блокируется до поступления данных print(‘server %s got [%s]’ % (mypid, data)) # вывод в окно терминала

def client1():

mypid = os.getpid()

redirectOut()

for i in range(3):

print(‘client %s: %s’ % (mypid, i)) # вывод в сокет

sys.stdout.flush() # иначе останется в буфере

# до завершения!

############################################################################ # перенаправление ввода в клиенте

############################################################################

def server2():

mypid = os.getpid() # простой сокет без буферизации

conn = initListenerSocket() # отправляет в поток ввода клиента

for i in range(3):

conn.send((‘server %s: %s\n’ % (mypid, i)).encode())

def client2():

mypid = os.getpid()

redirectIn()

for i in range(3):

data = input() # ввод из сокета

print(‘client %s got [%s]’ % (mypid, data)) # вывод в окно терминала

############################################################################ # перенаправление ввода и вывода в клиенте, клиент является

#   клиентом для сокета

############################################################################

def server3():

mypid = os.getpid()

conn = initListenerSocket() # ждать подключения клиента

file = conn.makefile(‘r‘) # принимает от print(), передает в input()

for i in range(3): # readline блокируется до появления данных data = file.readline().rstrip()

conn.send((‘server %s got [%s]\n’ % (mypid, data)).encode())

def client3():

mypid = os.getpid()

redirectBothAsClient()

for i in range(3):

print(‘client %s: %s’ % (mypid, i)) # вывод в сокет

data = input() # ввод из сокета: выталкивает!

sys.stderr.write(‘client %s got [%s]\n’ % (mypid, data)) # не был

# перенаправлен

############################################################################

#   перенаправление ввода и вывода в клиенте, клиент является

#   сервером для сокета

############################################################################

def server4():

mypid = os.getpid()

sock = socket(AF_INET, SOCK_STREAM)

sock.connect((host, port))

file = sock.makefile(‘r’)

for i in range(3):

sock.send((‘server %s: %s\n’%(mypid,i)).encode()) # передать

# в input()

data = file.readline().rstrip() # принять от print()

print(‘server %s got [%s]’ % (mypid, data)) # результат в терминал

def client4():

mypid = os.getpid()

redirectBothAsServer() # играет роль сервера в этом режиме

for i in range(3):

data = input() # ввод из сокета: выталкивает выходной буфер! print(‘client %s got [%s]’ % (mypid, data)) # вывод в сокет sys.stdout.flush() # иначе последняя порция данных останется

# в буфере до завершения!

############################################################################

#   перенаправление ввода и вывода в клиенте, клиент является клиентом

#   для сокета, сервер первым инициирует обмен

############################################################################

def server5():

mypid = os.getpid() # тест № 4, но соединение принимает сервер

conn = initListenerSocket() # ждать подключения клиента

file = conn.makefile(‘r‘) # принимает от print(), передает в input()

for i in range(3):

conn.send((‘server %s: %s\n’ % (mypid, i)).encode())

data = file.readline().rstrip()

print(‘server %s got [%s]’ % (mypid, data))

def client5():

mypid = os.getpid()

s = redirectBothAsClient() # играет роль клиента в этом режиме for i in range(3):

data = input() # ввод из сокета: выталкивает выходной буфер!

print(‘client %s got [%s]’ % (mypid, data)) # вывод в сокет sys.stdout.flush() # иначе последняя порция данных останется # в буфере до завершения!

############################################################################ # номер выполняемого теста определяется аргументом командной строки

############################################################################

if __name__ == ‘__main__’:

server = eval(‘server’ + sys.argv[1])

client = eval(‘client’ + sys.argv[1]) # клиент в этом процессе multiprocessing.Process(target=server).start() # сервер

# в новом процессе client() # переустановить потоки в клиенте

#import time; time.sleep(5) # проверка эффекта выталкивания

# буферов при выходе

Запустим тестовый сценарий, указав номер клиента и сервера в командной строке, чтобы проверить работу инструментов модуля. В сообщениях отображаются числовые идентификаторы процессов, а в квадратных скобках отображаются сообщения, переданные (дважды, если вложенные) через потоки ввода-вывода, подключенные к сокетам:

C:\\PP4E\Internet\Sockets> test-socket_stream_redirect.py 1

server 3844 got [client 1112: 0]

server 3844 got [client 1112: 1]

server 3844 got [client 1112: 2]

C:\\PP4E\Internet\Sockets> test-socket_stream_redirect.py 2

client 5188 got [server 2020: 0]

client 5188 got [server 2020: 1]

client 5188 got [server 2020: 2]

C:\\PP4E\Internet\Sockets> test-socket_stream_redirect.py 3

client 7796 got [server 2780 got [client 7796: 0]]

client 7796 got [server 2780 got [client 7796: 1]]

client 7796 got [server 2780 got [client 7796: 2]]

C:\…\PP4E\Internet\Sockets> test-socket_stream_redirect.py 4

server 4288 got [client 3852 got [server 4288: 0]]

server 4288 got [client 3852 got [server 4288: 1]]

server 4288 got [client 3852 got [server 4288: 2]]

C:\…\PP4E\Internet\Sockets> test-socket_stream_redirect.py 5

server 6040 got [client 7728 got [server 6040: 0]]

server 6040 got [client 7728 got [server 6040: 1]]

server 6040 got [client 7728 got [server 6040: 2]]

Если сопоставить вывод сценария с программным кодом, чтобы понять, как передаются сообщения между клиентом и сервером, можно будет увидеть, что функции print и input в функциях клиентов в конечном итоге обращаются к сокетам в другом процессе. В функциях клиентов связь с сокетами остается практически незаметной.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий