Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 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.

like image 291
Eli Courtwright Avatar asked Aug 04 '11 18:08

Eli Courtwright


People also ask

What is the use of socket Recvfrom () method?

recvfrom(data, address) − This method receives data from the socket. Two pair (data, address) value is returned by this method. Data defines the received data and address specifies the address of socket sending the data.

What is socket Sock_stream?

SOCK_STREAM. Provides sequenced, two-way byte streams with a transmission mechanism for stream data. This socket type transmits data on a reliable basis, in order, and with out-of-band capabilities. In the UNIX domain, the SOCK_STREAM socket type works like a pipe.

How is Python socket different from file handle?

A socket is much like a file, except that a single socket provides a two-way connection between two programs. You can both read from and write to the same socket. If you write something to a socket, it is sent to the application at the other end of the socket.

What is socket Af_inet in Python?

AF_INET is the Internet address family for IPv4. SOCK_STREAM is the socket type for TCP, the protocol that will be used to transport messages in the network. The . bind() method is used to associate the socket with a specific network interface and port number: # echo-server.py # ... with socket.


1 Answers

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
like image 195
abbot Avatar answered Sep 22 '22 14:09

abbot