Simple TCP server written in Python, using socket and select
Saturday, February 7, 2009 4:20:00 AM
Introduction
I'm supposing you know Python somewhat well, and know how TCP/IP works. No need to be an expert, but a good understanding of how it works helps a lot. For testing, a telnet or netcat client is handy.
Before writing the code below, I read these pages:
- Socket Programming in Python - Shows how to write a very basic UDP server with Python sockets.
- socket - Low-level networking interface - The example in Python official documentation shows how to write a very basic TCP server.
- select - Waiting for I/O completion
How it works
At first, the server will create a socket object to bind itself to a port on local machine, accepting connections from any IP. This socket can only be used to listen for new connections; no data is sent or received through this socket.
Then the server enters an infinite loop, using select() to wait for I/O.
When a new connection is received, the .accept() method will return a new socket object that is specific for this new connection. This new socket can be used for sending and receiving data, while the old listening socket is kept intact. This new socket is added to a list of currently open sockets.
When data is received from an open socket, the data is read using .recv() and then printed to the server terminal. If .recv() returns no data (empty string), it means that the connection was closed (unfortunately, this is not described at Python documentation, but it is described at recv(2) man page).
No exception handling is done. Actually, right now I don't even know if this code can generate exceptions. (information regarding this is very appreciated)
The code
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket, select
# List of socket objects that are currently open
open_sockets = []
# AF_INET means IPv4.
# SOCK_STREAM means a TCP connection.
# SOCK_DGRAM would mean an UDP "connection".
listening_socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
# The parameter is (host, port).
# The host, when empty or when 0.0.0.0, means to accept connections for
# all IP addresses of current machine. Otherwise, the socket will bind
# itself only to one IP.
# The port must greater than 1023 if you plan running this script as a
# normal user. Ports below 1024 require root privileges.
listening_socket.bind( ("", 1234) )
# The parameter defines how many new connections can wait in queue.
# Note that this is NOT the number of open connections (which has no limit).
# Read listen(2) man page for more information.
listening_socket.listen(5)
while True:
# Waits for I/O being available for reading from any socket object.
rlist, wlist, xlist = select.select( [listening_socket] + open_sockets, [], [] )
for i in rlist:
if i is listening_socket:
new_socket, addr = listening_socket.accept()
open_sockets.append(new_socket)
else:
data = i.recv(1024)
if data == "":
open_sockets.remove(i)
print "Connection closed"
else:
print repr(data)
Testing
You can write a Python client to test this server, you can just use netcat. Run the following command at your shell:
nc 127.0.0.1 1234Then type something and press Enter. To close the netcat, press Ctrl-C.
Try opening multiple simultaneous connections, and also from different hosts, just to see that things actually work, even with such simple code.
And that's all, folks! I hope this code is useful for someone else, as it shows how select() and sockets can be combined. Have fun!








Mad Scientistqlue # Wednesday, February 11, 2009 4:36:16 PM
Unregistered user # Wednesday, August 19, 2009 11:07:17 AM
Denilson Figueiredo de SáCrazyTerabyte # Thursday, August 20, 2009 12:06:35 AM
If you are working with web applications (that should run inside a web browser), you should (I mean... you MUST) stick to JavaScript for the client-side.
Thus, by your description it seems you want this:
http://en.wikipedia.org/wiki/Comet_(programming)
Sorry, I can't help you more than this.
Unregistered user # Thursday, August 20, 2009 10:01:13 AM
Unregistered user # Tuesday, July 6, 2010 1:36:38 PM
Denilson Figueiredo de SáCrazyTerabyte # Tuesday, July 6, 2010 2:38:30 PM
No, I did no modifications to that code. I wrote that as a simple "proof-of-concept", and haven't improved nor stress-tested it.
If you have improvements, comments, or anything else, please feel free to post at your blog or at your github (or bitbucket), and I'll add a link here.
Unregistered user # Tuesday, July 6, 2010 7:55:12 PM
Unregistered user # Thursday, June 23, 2011 9:47:52 AM