C:\...\PP4E\Internet\Sockets> socket-unbuff-server.py
accepting...
receiving...
b'spam\r\n'
receiving...
b'eggs\r\n'
receiving...
b'ham\n'
In other words, even when line buffering is requested, socket wrapper file writes (and
by association, prints) are buffered until the program exits, manual flushes are reques-
ted, or the buffer becomes full.
Solutions
The short story here is this: to avoid delayed outputs or deadlock, scripts that might
send data to waiting programs by printing to wrapped sockets (or for that matter, by
using print or sys.stdout.write in general) should do one of the following:
- Call sys.stdout.flush periodically to flush their printed output so it becomes
available as produced, as shown in Example 12-11. - Be run with the -u Python command-line flag, if possible, to force the output stream
to be unbuffered. This works for unmodified programs spawned by pipe tools such
as os.popen. It will not help with the use case here, though, because we manually
reset the stream files to buffered text socket wrappers after a process starts. To
prove this, uncomment Example 12-11’s manual flush calls and the sleep call at its
end, and run with -u: the first test’s output is still delayed for 5 seconds. - Use threads to read from sockets to avoid blocking, especially if the receiving pro-
gram is a GUI and it cannot depend upon the client to flush. See Chapter 10 for
pointers. This doesn’t really fix the problem—the spawned reader thread may be
blocked or deadlocked, too—but at least the GUI remains active during waits. - Implement their own custom socket wrapper objects which intercept text write
calls, encode to binary, and route to a socket with send calls; socket.makefile is
really just a convenience tool, and we can always code a wrapper of our own for
more specific roles. For hints, see Chapter 10’s GuiOutput class, the stream redi-
rection class in Chapter 3, and the classes of the io standard library module (upon
which Python’s input/output tools are based, and which you can mix in custom
ways). - Skip print altogether and communicate directly with the native interfaces of IPC
devices, such as socket objects’ raw send and recv methods—these transfer data
immediately and do not buffer data as file methods can. We can either transfer
simple byte strings this way or use the pickle module’s dumps and loads tools to
convert Python objects to and from byte strings for such direct socket transfer
(more on pickle in Chapter 17).
Making Sockets Look Like Files and Streams | 837