Файлы-обертки сокетов в текстов ом режиме принимают также значение 1 в аргументе, определяющем режим буферизации, что позволяет определить режим по строч ной буфери за ции вместо режима полной буферизации:
> >> from socket import *
> >> s = socket()
> >> f = s.makefile(‘w‘, 1) # то же, что и buffering=1, но действует
# как режим полной буферизации!
Похоже, что этот режим ничем не отличается от режима полной буферизации и по-прежнему требует вручную выталкивать выходной буфер, чтобы обеспечить передачу строк по мере их вывода. Рассмотрим простые сценарии сервера и клиента, представленные в примерах 12.13 и 12.14. Сервер просто читает три сообщения, используя интерфейс сокетов непосредственно.
Пример 12.13. PP4E\Internet\Sockets\socket-unbuff-server.py
from socket import * # читает три сообщения непосредственно из сокета
sock = socket()
sock.bind((», 60000))
sock.listen(5)
print(‘accepting…’)
conn, id = sock.accept() # блокируется, пока не подключится клиент
for i in range(3):
print(‘receiving…’)
msg = conn.recv(1024) # блокируется, пока не поступят данные print(msg) # выведет все строки сразу, если не выталкивать
# буфер вручную
Клиент, представленный в примере 12.14, отправляет три сообщения. Первые два отправляются через файл-обертку сокета, а последнее — прямым обращением к сокету. Вызовы метода flush здесь закомментированы, но оставлены, чтобы вы могли поэкспериментировать с ними, а вызовы функции sleep заставляют сервер ждать поступления данных.
Пример 12.14. PP4\Internet\Sockets\socket-unbuff-client.py
import time # отправляет три сообщения через файл-обертку и сокет from socket import *
sock = socket() # по умолчанию=AF_INET, SOCK_STREAM (tcp/ip) sock.connect((‘localhost’, 60000))
file = sock.makefile(‘w‘, buffering=1) # по умолчанию=полная буферизация, # 0=ошибка, 1 не включает построчную # буферизацию!
print(‘sending data1′)
file.write(‘spam\n‘)
time.sleep(5) # следующий вызов flush() должен вызвать немедленную передачу #file.flush() # раскомментируйте вызовы flush(), чтобы увидеть разницу print(‘sending data2′) # дополнительный вывод в файл не приводит print(‘eggs‘, file=file) # к выталкиванию буфера
time.sleep(5) # вывод будет принят сервером только после выталкивания буфера
#file.flush() # или после завершения
print(‘sending data3′) # низкоуровневый двоичный интерфейс выполняет передачу
sock.send(b‘ham\n‘) # немедленно, эта строка будет принята первой, если
time.sleep(5) # в первых двух случаях не выталкивать буферы вручную!
Запустите сначала сервер в одном окне, а затем клиента — в другом (или, в Unix-подобных системах, запустите сначала сервер в фоновом режиме). Ниже показан вывод в окне сервера — передача сообщений, отправленных через файл-обертку сокета, откладывается до завершения программы-клиента, а передача данных, отправляемых через низкоуровневый интерфейс сокета, выполняется немедленно:
C:\…\PP4E\Internet\Sockets> socket-unbuff-server.py
accepting…
receiving…
b’ham\n’
receiving…
b’spam\r\neggs\r\n’
receiving…
b»
В окне клиента строки «sending» появляются через каждые 5 секунд. Третье сообщение появится в окне сервера через 10 секунд, а передача первого и второго сообщений, отправленных через файл-обертку, будет отложена до завершения клиента (на 15 секунд), потому что файл-обертка действует в режиме полной буферизации. Если в клиенте раскомментировать вызовы метода flush, все три сообщения по очереди будут появляться в окне сервера с интервалом 5 секунд (третье сообщение появится после второго):
C:\…\PP4E\Internet\Sockets> socket-unbuff-server.py
accepting…
receiving…
b’spam\r\n’
receiving…
b’eggs\r\n’
receiving…
b’ham\n’
Иными словами, даже когда запрошена построчная буферизация, вывод в файл-обертку сокета (и, соответственно, вывод функции print) будет сохраняться в буфере, пока программа не завершит работу, или пока выходной буфер не будет вытолкнут вручную, или пока буфер не переполнится.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011