Foundations of Python Network Programming

(WallPaper) #1

Chapter 7 ■ Server arChiteCture


134


Listing 7-10. Answer a Single Client, Whose Socket Is the stdin/stdout/stderr


#!/usr/bin/env python3


Foundations of Python Network Programming, Third Edition


https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter07/in_zen1.py


Single-shot server for the use of inetd(8).


import socket, sys, zen_utils


if name == 'main':
sock = socket.fromfd(0, socket.AF_INET, socket.SOCK_STREAM)
sys.stdin = open('/dev/null', 'r')
sys.stdout = sys.stderr = open('log.txt', 'a', buffering=1)
address = sock.getpeername()
print('Accepted connection from {}'.format(address))
zen_utils.handle_conversation(sock, address)


This script is careful to replace the Python standard input, output, and error objects with more appropriate open
files because you rarely want raw tracebacks and status messages—that either Python or one of its libraries might
direct toward standard out or especially standard error—interrupting your conversation with the client. Note that
this maneuver fixes only I/O attempted from within Python itself because it touches the file objects only inside of
sys but not the real file descriptors. If your server calls any low-level C libraries that do their own standard I/O, then
you will want to close the underlying file descriptors 0, 1, and 2 as well. However, in that case, you are beginning to
undertake the kind of sandboxing that is really better accomplished through supervisord, a daemonization module,
or platform-style containerization as described in the previous “A Few Words About Deployment” section.
You can test Listing 7-10 at your normal user command line, so long as the port you have chosen is not a low-
numbered one, by running inetd -d inet.conf against a tiny configuration file that contains the line given earlier
and then connecting to the port as usual with client.py.
The other pattern is to specify the string wait in the fourth field of your inetd.conf entry, which means that your
script will be given the listener socket itself. This gives your script the task of calling accept() for the client that is
currently waiting. The advantage of this is that your server can then choose to stay alive and keep running accept()
to receive further client connections without inetd having to be involved. This can be more efficient than starting
a whole new process for every single incoming connection. If clients stop connecting for a while, your server can
feel free to exit() to reduce the server machine’s memory footprint until a client needs the service again; inetd will
detect that your service has exited and take over the job of listening again.
Listing 7-11 is designed to be used in wait mode. It is capable of accepting new connections forever, but it can
also time out and exit—absolving the server of the need to keep it in memory any longer—if several seconds go by
without any new client connections.


Listing 7-11. Answer One or More Client Connections, but Eventually Get Bored and Time Out


#!/usr/bin/env python3


Foundations of Python Network Programming, Third Edition


https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter07/in_zen2.py


Multi-shot server for the use of inetd(8).


import socket, sys, zen_utils


if name == 'main':
listener = socket.fromfd(0, socket.AF_INET, socket.SOCK_STREAM)
sys.stdin = open('/dev/null', 'r')
sys.stdout = sys.stderr = open('log.txt', 'a', buffering=1)

Free download pdf