As a complement to the network client tutorial, this tutorial shows how to implement a simple web server in Python. To be sure, this is no substitute for Apache or Zope. There are also more robust ways to implement web services in Python, using modules like BaseHTTPServer. This server uses the socket module exclusively.
You will recall that the socket module is the backbone of most Python web service modules. As with the simple network client, building a server with it illustrates the basics of web services in Python transparently. BaseHTTPServer itself imports the socket module to affect a server.
By way of review, All network transactions happen between clients and servers. In most protocols , the clients ask a certain address and receive data.
Within each address, a multitude of servers can run. The limit is in the hardware. With sufficient hardware (RAM, processor speed, etc.), the same computer can serve as a web server, an ftp server, and mail server (pop, smtp, imap, or all of the above) all at the same time. Each service is associeted with a port. The port is bound to a socket. The server listens to its associated port and gives information when requests are received on that port.
So to affect a network connection you need to know the host, the port, and the actions allowed on that port. Most web servers run on port 80. However, in order to avoid conflict with an installed Apache server, our web server will run on port 8080. In order to avoid conflict with other services, it is best to keep HTTP services on port 80 or 8080. These are the two most common. Obviously, if these are used, you must find an open port and alert users to the change.
As with the network client, you should note that these addresses are the common port numbers for the different services. As long as the client asks for the correct service on the right port at the right address, communication will still happen. Google's mail service, for example, did not initially run on the common port numbers but, because they know how to access their accounts, users can still get their mail.
Unlike the network client, all variables in the server are hardwired. Any service that is expected to run constantly should not have the variables of its internal logic set at the command line. The only variation on this would be if, for some reason, you wanted the service to run occasionally and on various port numbers. If this were the case, however, you would still be able to watch the system time and change bindings accordingly.
So our sole import is the socket module.
Next, we need to declare a few variables.
As already mentioned, the server needs to know the host to which it is to be associated and the port on which to listen. For our purposes, we shall have the service apply to any host name at all.
The port, as mentioned earlier, will be 8080. So note that, if you use this server in conjunction with the network client, you will need to change the port number used in that program.
host = '' port = 8080
Whether to request information or to serve it, in order to access the Internet, we need to create a socket. The syntax for this call is as follows:
<variable> = socket.socket(<family>, <type>)
The recognised socket families are:
- AF_INET: IPv4 protocols (both TCP and UDP)
- AF_INET6: IPv6 protocols (both TCP and UDP)
- AF_UNIX: UNIX domain protocols
The socket type refers to the type of communication used through the socket. The five socket types are as follows:
- SOCK_STREAM: a connection-oriented, TCP byte stream
- SOCK_DGRAM: UDP transferral of datagrams (self-contained IP packets that do not rely on client-server confirmation)
- SOCK_RAW: a raw socket
- SOCK_RDM: for reliable datagrams
- SOCK_SEQPACKET: sequential transfer of records over a connection
So let's create a socket and assign it to a variable.
c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
After creating the socket, we then need to set the socket options. For any socket object, you can set the socket options by using the setsockopt() method. The syntax is as follows:
socket_object.setsockopt(level, option_name, value) For our purposes, we use the following line:
c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
The term 'level' refers to the categories of options. For socket-level options, use SOL_SOCKET. For protocol numbers, one would use IPPROTO_IP. SOL_SOCKET is a constant attribute of the socket. Exactly which options are available as part of each level are determined by your operating system and whether you are using IPv4 or IPv6.
The documentation for Linux and related Unix systems can be found in the system documentation. The documentation for Microsoft users can be found on the MSDN website. As of this writing, I have not found Mac documentation on socket programming. As Mac is roughly based upon BSD Unix, it is likely to implement a full complement of options.
In order to ensure reusability of this socket, we use the SO_REUSEADDR option. One could restrict the server to only run on open ports, but that seems unnecessary. Do note, however, that if two or more services are deployed on the same port, the effects are unpredictable. One cannot be certain which service will receive which packet of information.
Finally, the '1' for a value is the value by which the request on the socket is known in the program. In this way, a program can listen on a socket in very nuanced ways.
After creating the socket and setting its options, we need to bind the port to the socket.
The binding done, we now tell the computer to wait and to listen on that port.
If we want to give feedback to the person who calls the server, we could now enter a print command to confirm that the server is up and running.
Having setup the server, we now need to tell Python what to do when a request is made on the given port. For this we reference the request by its value and use it as the argument of a persistent while loop.
When a request is made, the server should accept the request and create a file object to interact with it.
while 1: csock, caddr = c.accept() cfile = csock.makefile('rw', 0)
In this case, the server uses the same port for reading and writing. Therefore, the makefile method is given an argument 'rw'. The null length of the buffer size simply leaves that part of the file to be determined dynamically.
Unless we want to create a single-action server, the next step is to read input from the file object. When we do that, we should be careful to strip that input of excess whitespace.
line = cfile.readline().strip()
The request will come in the form of an action, followed by a page, the protocol, and the version of the protocol being used. If one wants to serve a web page, one splits this input to retrieve the page requested and then reads that page into a variable which is then written to the socket file object. A function for reading a file into a dictionary can be found in the blog.
In order to make this tutorial a bit more illustrative of what one can do with the socket module, we will forego that part of the server and instead show how one can nuance the presentation of data. Enter the next several lines into the program.
cfile.write('HTTP/1.0 200 OK\n\n') cfile.write('<html><head><title>Welcome %s!</title></head>' %(str(caddr))) cfile.write('<body><h1>Follow the link...</h1>') cfile.write('All the server needs to do is ') cfile.write('to deliver the text to the socket. ') cfile.write('It delivers the HTML code for a link, ') cfile.write('and the web browser converts it. <br><br><br><br>') cfile.write('<font size="7"><center> <a href="http://python.about.com/index.html">Click me!</a> </center></font>') cfile.write('<br><br>The wording of your request was: "%s"' %(line)) cfile.write('</body></html>')
If one is sending a web page, the first line is a nice way of introducing the data to a web browser. If it is left out, most web browsers will default to rendering HTML. However, if one includes it, the 'OK' must be followed by two new line characters. These are used to distinguish the protocol information from the page content.
The syntax of the first line, as you can probably surmise, is protocol, protocol version, message number, and status. If you have ever gone to a web page that has moved, you have probably received a 404 error. The 200 message here is simply the affirmative message.
The rest of the output is simply a web page broken up over several lines. You will note that the server can be programmed to use user data in the output. The final line reflects the web request as it was received by the server.
Finally, as the closing acts of the request, we need to close the file object and the server socket.
Now save this program under a recognisable name. After you call it with 'python program_name.py', if you programmed a message to confirm the service as running, this should print to the screen. The terminal will then seem to pause. All is as it should be. Open your web browser and go to localhost:8080. You should then see the output of the write commands we gave. Please note that, for the sake of space, I did not implement error handling in this program. However, any program released into the 'wild' should. See "Error Handling in Python" for more.