我可以让socket.makefile与常规文件具有相同的读取语义吗?

15 投票
1 回答
18310 浏览
提问于 2025-04-16 22:54

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的文档,"可选的模式和缓冲区大小参数的解释方式与内置的file()函数相同." 但是我的原始文件示例即使我写open(fname, "r+b", 0)也能正常工作,而我却找不到一种方法可以通过socket伪文件返回所有可用的数据,直到指定的字节数。

如果我直接使用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 个回答

27

这里的问题是,client.read() 会尝试从当前位置读取数据直到文件结束符(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

撰写回答