We previously wrote a brief introduction to networking, however networking is a larger topic than that, so we've decided to produce a larger series to try and do the subject justice.
Binding services to sockets
We're going to illustrate binding this service from Python since shell is not appropriate, and C is too verbose.
However we're going to use the low-level Python bindings rather than the socketserver class, to show how it works beneath the layers of abstraction.
Python's bindings are in the socket module, we're doing a qualified import to resemble the API from C.
from socket import socket, AF_INET, SOCK_STREAM
There's lots of setup stages involved in making a connection available, following the general builder pattern, rather than having a single syscall that takes a huge number of options, as it is more extensible.
socket() syscall takes the parameters family, socket type and
protocol. This describes exactly what kind of socket to create. This will be
explained in greater detail later, but here we create a socket with an IP
address which acts as a stream.
s = socket(AF_INET, SOCK_STREAM, 0)
One side of the connection needs to bind to a port, so that the other can connect to it. The address specifies which address your machine has, that the service should be served on. '0.0.0.0' means "all my addresses", but we're going to use '127.0.0.1' which means "only my internal address" so that we can't have anything on the network connect to it:
addr = '127.0.0.1'
A port number also needs to be chosen to provide the service. The address denotes how to find the service, while the port represents which of the potential services available should be connected to.
port = 12345 s.bind((addr, port))
The call to listen() is further configuration, of how many connections to buffer before refusing new connections.
s.listen(0) while True:
The accept() call blocks until there are available connections to service and returns some information about where the connection was from.
conn, hostaddr = s.accept()
The python connection object wraps a file descriptor that can be used as any normal file, so we do some conversion so we can use it as one.
f = conn.makefile('rw') while True: f.write('> ') f.flush() line = f.readline() if not line: break if line == 'to me\n': print('to you') elif line == 'oh dear\n': print('oh dear oh dear') break conn.close()
We can demonstrate that this works with the netcat command.
$ python chuckle.py & $ nc 127.0.0.1 12345 > to me to you > oh dear oh dear oh dear ^C $
We can make this service start on boot by creating and enabling a systemd unit like this:
$ sudo install -D -m755 chuckle.py /usr/local/libexec/chuckle.py $ sudo tee /etc/systemd/system/chuckle.service >/dev/null <<'EOF' [Unit] Description=Chuckle server [Service] ExecStart=/usr/local/libexec/chuckle.py [Install] WantedBy=multi-user.target EOF $ sudo systemctl daemon-reload $ sudo systemctl enable chuckle.service
Connecting to services
#!/usr/bin/python #connect.py import sys
As before we need the same socket definitions to make a socket.
from socket import socket, AF_INET, SOCK_STREAM s = socket(AF_INET, SOCK_STREAM, 0)
Now instead of binding and listening we call connect().
The rest is just copying input from your terminal to the socket. Note that this is unidirectional and line based, while netcat is bidirectional and asynchronous.
f = s.makefile('w') while True: line = sys.stdin.readline() if not line: break f.write(line) f.flush()
It is simpler to create connections than bind services, but the address conventions are the same.
Our existing service should still be running, so we can connect to it just like we did before with netcat.
$ python connect.py > to me to you > oh dear oh dear oh dear ^C $