Могу ли я получить файл socket.makefile с той же семантикой чтения, что и у обычного файла? - PullRequest
13 голосов
/ 04 августа 2011

Файловые объекты Python имеют метод read, который принимает необязательный аргумент размера, который, по сути, является максимальным числом возвращаемых байтов.Например:

fname = "message.txt"
open(fname, "w").write("Hello World!")
print open(fname).read()   # prints the entire file contents
print open(fname).read(5)  # print "Hello"
print open(fname).read(99) # prints "Hello World!"

Таким образом, даже если наш файл содержит менее 99 символов, вызов read(99) немедленно возвращается со всеми доступными данными.

Я хотел бы получитьэто поведение для файловых объектов, возвращаемых из socket.makefile .Но если я скажу:

import socket
ADDR = ("localhost", 12345)

listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.bind(ADDR)
listener.listen(1)

client = socket.create_connection(ADDR)
cf = client.makefile("r+b", bufsize=0)

server, client_addr = listener.accept()
sf = server.makefile("r+b", bufsize=0)

sf.write("Hello World!")
sf.flush()
print cf.read(99)         # hangs forever

Согласно socket.makefile документам, "Необязательные аргументы mode и bufsize интерпретируются так же, как встроенная функция file ()." Но мой оригинальный пример файла работает, даже когда я говорю open(fname, "r+b", 0), тогда как я не могу найти способ вернуть все доступные данные до указанного числа байтов с помощью псевдофайла сокета.

Кажется, это работает очень хорошо, если я просто использую socket.recv:

import socket
ADDR = ("localhost", 12345)

listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.bind(ADDR)
listener.listen(1)

client = socket.create_connection(ADDR)
server, client_addr = listener.accept()

server.sendall("Hello World!")
print client.recv(99)           # prints "Hello World!"

Так есть ли способ заставить эту работу работать с socket.makefile, или этот «расширенный» функционал просто недоступен?

РЕДАКТИРОВАТЬ: Python 3.2, кажется, работает правильно, хотя синтаксис аргумента socket.makefile, похоже, изменился:

import socket
ADDR = ("localhost", 12345)

listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.bind(ADDR)
listener.listen(1)

client = socket.create_connection(ADDR)
cf = client.makefile("rwb", buffering=0)

server, client_addr = listener.accept()
sf = server.makefile("rwb", buffering=0)

sf.write(b"Hello World!")
sf.flush()
print(cf.read(99))         # prints "Hello World!"

Я не копался вИсходный код еще предстоит выяснить разницу между этими двумя версиями, но это может быть подсказка.

1 Ответ

24 голосов
/ 04 августа 2011

Проблема здесь в том, что client.read() пытается прочитать из текущей позиции в EOF, но EOF для сокета появляется только тогда, когда другая сторона закрывает соединение. recv, с другой стороны, вернет все данные, готовые для чтения (если они есть), или может заблокироваться в соответствии с настройками блокировки и тайм-аута.

Сравните с этим:

import socket
ADDR = ("localhost", 12345)

listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.bind(ADDR)
listener.listen(1)

client = socket.create_connection(ADDR)
cf = client.makefile("r+b", bufsize=0)

server, client_addr = listener.accept()
sf = server.makefile("r+b", bufsize=0)

sf.write("Hello World!")
sf.flush()
sf.close()
server.close()
print cf.read(99)         # does not hang
...