Can I get a socket.makefile to have the same read semantics as a regular file?

Viewed 17765

Python file objects have a read method which takes an optional size argument, which is basically the maximum number of bytes 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.

1 Answers
Related