Simple socket server in Python

Sockets are one of the most commonly used communication tools on computers, but they can be hard to understand at first. If you break it down however, it’s easy to understand how to use them. In this tutorial we’re going to learn how to implement a simple socket server in Python, that will accept a single client connection.

Note that I will use Mac OS X for this tutorial, but you can use any other OS.

Let’s write a class SocketServer that will handle the socket communication.

Create and set up the socket

The first step is to create a socket that will listen to an incoming connection. To do so, we need to import the module socket:

Then, create the socket object:

A few things to note here (more information can be found on the man page of socket):

  • AF_INET specifies the protocol family used for the communication. In most cases, you’ll want to use AF_INET for IPv4 internet protocols.
  • SOCK_STREAM is used for a TCP communication. That’s what you’ll want to use in case you want a connection-based communication.
  • setsockopt() is used to specify options on the socket. Here, we set the option SO_REUSEADDR, which indicates that the system can reuse this socket (unless it’s being actively used). It’s not necessary and you can omit it if you’d like.

Bind the socket and listen

So far, our socket doesn’t have an address assigned to it. To bind it to an address, we need to give it a host address and a port. If you’re only going to use the server locally, then you should select 127.0.0.1 (localhost) as a host. Otherwise, you can use 0.0.0.0, and it will be accessible from other machines on your local network. You can use any port you’d like, but be aware that most low-value ports (under 1000) are likely to be used by common protocols. You can take a look at /etc/services to get a list of defined protocols and ports.

Bind the socket with:

Now, we need to set up the socket so that it waits for an incoming connection. We will later use accept() to accept a connection. The number you give to listen() specifies how many connections can be queued for this socket. As we’ll only have one client in this tutorial, we can just set it to 1.

At this point, you can check if the server is listening on the port you set, by starting the Python program and using netcat in zero I/O mode. If your server is running, netcat will say that the connection was successful:

Accept and handle a connection

We are now ready to accept an incoming connection and handle the communication. What we’ll do is simple: after accepting the connection, we’ll send a copy of the received data to the client. When we receive the string “quit”, we’ll stop the server and close the socket. We’ll also close the connection if we detect that the client is not connected anymore. Let’s put this part of the code in a function run_server().

Accept a connection

First of all, we need to accept the incoming connection. The function accept() blocks until a client connects to our server. When this happens, accept() returns a socket object (the client socket) and an address object that contains the host and port of the client (similar to how we had a address object defined as (host, port)  when we called bind()).

Receive and send data

At this point, we have a client connected and can start receiving data. We’ll implement a loop that will try to read data until the client is disconnected or we receive the string “quit”.

We could simply use recv() to receive data from the socket. However, this wouldn’t allow us to detect that the client has disconnected from the socket. You can use the function select() from the module select. You have to give it 3 lists (they can be empty):

  • sockets to check if available for reading
  • sockets to check if available for writing
  • sockets to check if available for an exceptional condition

select() will block until at least one file descriptor in the 3 lists is available, and return 3 lists containing subsets of the contents of the lists passed as parameters:

  • sockets available for reading
  • sockets available for writing
  • sockets with an error.

It is best to enclose the call to select() in a try block, to catch any error. In our case, we only care about write available, so that’s the only parameter we’ll give:

Now we can read data from the socket. If rdy_read > 0 and the length of the data we read is 0, then we know that the client has disconnected and we can close the socket. Otherwise, we process the incoming data:

Nothing complicated if we receive valid data, we just send a copy of it to the client. If the data is “quit”, then we set stop to True so that we exit the server loop. Note that we use rstrip() on the data before comparing it, because it originally ends with a line return.

Test the server

To test our server, start the Python script, and open a second terminal where we’ll use netcat to connect to it. Here I’m using the address localhost and port 2010; use the same address and port as the one your server is using.

On the terminal running localhost, type some text. Hitting enter will send the current text to the server. When you want to stop the Python server and netcat, simply type “quit” and hit enter.

Here is an example of execution:

Terminal with netcat
Terminal with netcat
Python socket server
Python socket server

Full code

As you can see, using sockets in Python is not complicated at all, and it’s a very powerful tool for communicating between processes or machines. And the great thing is that the functions we used are Linux system calls, so they’re basically the same in C, making it very easy to transfer what we learned in this article, to a C program.

Here is the full code of the Python socket server for reference:

 

Be the first to comment

%d bloggers like this: