Can I get a socket.makefile to have the same read semantics as a regular file?
Python file objects have a read method which takes an optional size argument, which is basically the maximum number of byt开发者_Python百科es to return. For example:
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!"
So even though our file has fewer than 99 characters, a call to read(99)
returns immediately with all of the available data.
I'd like to get this behavior on the file objects returned from socket.makefile. But if I say:
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
According to the socket.makefile
docs, "The optional mode and bufsize arguments are interpreted the same way as by the built-in file() function." But my original file example works even when I say open(fname, "r+b", 0)
, whereas I can't figure out a way to return all available data up to the specified number of bytes with a socket pseudo-file.
This seems to work perfectly well if I just use 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!"
So is there any way to make this work with socket.makefile
, or is this kind of "advanced" functionality simply not available?
EDIT: Python 3.2 seems to behave correctly, though the argument syntax to socket.makefile
appears to have changed:
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!"
I haven't dug into the source code yet to figure out the difference between these two versions, but that may be a hint.
The problem here is that client.read()
tries to read from current position to the EOF
, but the EOF for the socket appears only when the other side closes the connection. recv
on the other hand will return any data ready to be read (if there are any), or may block according to blocking and timeout settings.
Compare to this one:
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
精彩评论