How to create Client Socket and Server Socket with Python?

python

(Arrowsoft.IT Srls) #1

Hi everyone!

In this post, We would like to discuss in which manner the programmers can interact with one of the most badly entities in the programming world: the socket! :imp:

Due to the demoniac nature behind the socket, We want to stay too far as possible from it! Then, in this discussion, We will not use a low/medium level programming language (like C) that is the native way to use this infernal object.

Instead, We will use python that provide a good abstraction of all the features that socket can offer to Us!

##A smart overview
Sockets is everywhere around us! At least ALL the informations exchanged between peoples on the world (except the direct voice communication) are delivered through a socket! When you load an HTML page, also when you call on the VoIP or simply when you send a message to venus or pluton you might use a socket.

But, what is a socket? As wikipedia says,

“A socket is an internal endpoint for sending or receiving data at a single node in a computer network. Concretely, it is a representation of this endpoint in networking software (protocol stack), such as an entry in a table (listing communication protocol, destination, status, etc.), […]”

But, I don’t want to pester you with too many technical details because I assume that if you are on this post, and you have searched this kind of information (like googling socket python) you already know what sockets is and how socket works (in theory).

Then, when We talk about socket (in this post) We must only imagine that on our computer we can open a hole identified by an unique id (port number and binding address) on which another computer can be connected on and in which another computer (remote endpoint) can send the data!

That is! For the purpose of this article, a socket is an hole!

An hole with name (binding address aka IP on which computer will listen for remote connection) and port (internal number on which the computer will answer)! The presence of the port number allow us to imagine that a computer can have multiple listener on the same address (but not on the same port!).

Each computer can be a listener or/and a client. Of course, the listener wait for incoming socket and the client create a connection to a listener in order to send data.
The communication is always bi-directional.

Socket type

There are many socket types, each of type with it own features and properties. For example, there are socket for connection less communication (UDP), socket for data streaming (SCTP) and socket for connection-oriented communication (TCP).
Here, We will only consider TCP socket that is the most common type used on our planet! TCP allow us to create a relatively secure (in term of delivery of the messages) connection between two computers.

In a TCP communication there are two socket interconnected by two pair of (ip,port) entry.

Hello World by Socket

The first example that we want to examine is the classic “Hello world” program, but in the hell environment of the socket!

The listener code

In the listener, we need to open the hole and wait for client that fill it with data! The first example show how to open one socket in order to accept one connection.

import socket

print "Prepare socket listener"
TCP_IP = '0.0.0.0'  #accept connection on all ip address of the machine
TCP_PORT = 1978 #accept connection only on port 1978
BUFFER_SIZE = 1024 # we can receive at max 1024 bytes at time

# we choosen to use TCP stream socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# we inform the SO to bypass TIME_WAIT 
# this mean that we can restart this program without waiting for
# TIME_WAIT elapsing. 
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind the socket to the address and to the port. 
# This listener will receive from kernel only packet with this 
# unique id in the header!

s.bind((TCP_IP, TCP_PORT))


print "Wait for client"
# Start the listener (wait for only 1 connection at time)
s.listen(1)
conn, addr = s.accept() #Wait for client 

# please note that the accept is sync function. the program will wait
# for kernel interrupt before go to the next instruction

# When a client arrive
print 'Connection address:', addr  

# Start reading data
while 1:
        data = conn.recv(BUFFER_SIZE)
        if not data:
                print "No other data. Socket closed"
                break;
        else:
                print data  # print the data in the console

If we start this simple script with python (save file with name like listener.py) you will see in the console the message “wait for client”. Be careful: If you use a port that is busy (like 80) the program will crash with an exception.

The client code

In the client, We need to connect to a the hole. Then, we need to use another kind of instruction. As first, we don’t need to listen or bind to a port. This because when We start a connection to a remote endpoint (listener), the kernel will assign to Us a ‘temporary port’ (ephemera) that will be used for the local hole in which program will receive the data sent by the listener.

The following code will create a connection to a remote endpoint and will send an hello world message to it!

import socket

print "Prepare client"
TCP_IP = '0.0.0.0'  #connect to localhost (start listener first!!)
TCP_PORT = 1978 #connect to 1978 port

# we choosen to use TCP stream socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# we inform the SO to bypass TIME_WAIT
# this mean that we can restart this program without waiting for
# TIME_WAIT elapsing.
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Start the connection and send the hello world messages
s.connect((TCP_IP,TCP_PORT)) #Wait for client
s.send("Hello world!!\n");

In the following image you can see how listener and client exchange their first message! :thinking:

###Some considerations
If you try to use this code, you will see that both program start, create a connection, exchange one packet of data and then die. This is not really usefully in the real world. We need to consider the bi-directional nature of the socket. When two endopoint create a connection, two stream is open on both side, the first one for input and the second one for output.

Then, we you have a socket you can image to use the standard read(), write() and close() functions! Also, if you use multiple thread you can write and read from the socket when you want (and when there is a data on the buffer).

A simple chat with socket

In order to implement a chat, we must consider just a thing: each endpoint can talk in any moment. So we need to use thread, on for waiting and printing data on the screen, and one for reading message from standard input in order to send to it the other side.

The Listener code

import threading
import socket

print "Prepare socket listener"
TCP_IP = '0.0.0.0'  #accept connection on all ip address of the machine
TCP_PORT = 1978 #accept connection only on port 1978
BUFFER_SIZE = 1024 # we can receive at max 1024 bytes at time

# we choosen to use TCP stream socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# we inform the SO to bypass TIME_WAIT
# this mean that we can restart this program without waiting for
# TIME_WAIT elapsing.
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind the socket to the address and to the port.
# This listener will receive from kernel only packet with this
# unique id in the header!

s.bind((TCP_IP, TCP_PORT))


print "Wait for client"
# Start the listener (wait for only 1 connection at time)
s.listen(1)
s, addr = s.accept() #Wait for client

# please note that the accept is sync function. the program will wait
# for kernel interrupt before go to the next instruction

# When a client arrive
print 'Connection address:', addr

def _sendth():
        while 1:
                msg = raw_input(" msg-> ");
                s.send(msg)
def _readth():
        while 1:
                data = s.recv(BUFFER_SIZE)
                if not data:
                        print "No other data. Socket closed"
                        exit()
                else:
                        print "INCOMING MESSAGE:",data  # print the data in the console


writeth = threading.Thread(target=_sendth)
writeth.start()

readth = threading.Thread(target=_readth)
readth.start()

The client code

import socket
import sys
import threading

print "Prepare client"
TCP_IP = '0.0.0.0'  #accept connection on all ip address of the machine
TCP_PORT = 1978 #accept connection only on port 1978
BUFFER_SIZE = 1024 # we can receive at max 1024 bytes at time

# Threads for read and write
readth = None
writeth = None


# we choosen to use TCP stream socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# we inform the SO to bypass TIME_WAIT
# this mean that we can restart this program without waiting for
# TIME_WAIT elapsing.
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

s.connect((TCP_IP,TCP_PORT)) #Wait for client

def _sendth():
        while 1:
                msg = raw_input(" msg-> ");
                s.send(msg)
def _readth():
        while 1:
                data = s.recv(BUFFER_SIZE)
                if not data:
                        print "No other data. Socket closed"
                        exit()
                else:
                        print "INCOMING MESSAGE:",data  # print the data in the console
writeth = threading.Thread(target=_sendth)
writeth.start()

readth = threading.Thread(target=_readth)
readth.start()

If you try to execute this code you will be able to exchange simple text message between two programs!