Table of Content

Introduction

Socket is a networking API widely available. You can find it on Windows and Unix. You can also find its language bindings for C, C++, Java, and Python.

An operating system typically avails an essential subset of the Socket API as its system calls like Unix systems, or as its APIs like Windows. So, the Socket API is a low-level API. The implication is that it takes greater effort to write a network application than a high-level API; however, it offers also greater flexibility and control on the computer network than the high-level API. For instance, we use a high-level Python API called Flask to create a Web server application in a few lines of code; however, in the Socket API it would take hundreds if not thousands or more lines of code. A great advantage of learning and using the Socket API is that we can implement new network protocols and high-level APIs with it. In addition, a grasp of the Socket API can help us understand basic concepts of computer networks and use high level APIs.

At its core, the Socket API allows us to interface with the service provided by the protocols in the layers of the protocol stack from the network access layer (a.k.a., the data link layer) to the transport layer.

In this short tutorial, we demonstrate the use of the Socket API’s Python binding and write Python programs to interface TCP/IP protocols, such as, the two prominent transport layer protocols, TCP and UDP, and the network layer protocol for the Internet, IP.

The Socket Object

Socket is a concept in the Socket API. A socket is an object representing an end point of communication. The end point of communication is typically part of an application process. Thus, we consider such an end point as a 3-tutle (a.k.a., a triple or triplet) of .

  • For TCP, the triplet is
  • For UDP, the triplet is

Interfacing with TCP

TCP provides a reliable byte stream transport service for the Internet.

  • It requires an explicit setup of a connection between two end points.
  • The connection appears to be bi-directional and renders the data being transmitted as a stream of bytes in both directions between the two end points.
  • TCP attempts to maintain the connection and make the connection appear to be reliable.

A TCP application typically follows a client-server model and has a server logic and a client logic. The server logic typically consists of

  1. creating a socket object for the TCP protocol, call it the server socket;
  2. binding the socket to a host address where a host address is a 2-tuple (a.k.a., a pair) of ;
  3. starting passively listening to incoming connections on the socket;
  4. accepting an incoming connection and getting the ``reliable byte-stream bi-directional connection’’ accessible by a new socket object;
  5. writing bytes to or reading bytes from the connection following the desired application protocol as intended; and finally,
  6. tearing down the connection cooperatively with the client and releasing the associated resources upon completion of the communication betweeh the server and client.

The client logic consists of

  1. creating a socket object for the TCP protocol, call it the client socket;
  2. attempting a connection to the end point representing the server representing by a pair of and getting the ``reliable byte-stream bi-directional connection’’ accessible by the client socket
  3. writing bytes to or reading bytes from the connection following the desired application protocol as intended; and finally,
  4. tearing down the connection cooperatively with the server and releasing the associated resources upon completion of the communication between the server and client.

The Hello Messaging Application

Let’s consider an application consisting of a server and a client. They exchange short messages over TCP. The server program is helloserver.py and the client program is helloclient.py. Below are rudimentary implementation of the two programs.

helloserver.py

import socket

def main():
    host = '127.0.0.1'
    port = 50001
    bufsize = 1024

    server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, \
            proto=0, fileno=None)
    serveraddr = (host, port)
    server.bind(serveraddr)
    server.listen()
    (connection, clientaddr) = server.accept()

    msg = 'Hey, I am the server. What\'s up, client?' 
    buf = msg.encode()
    connection.send(buf)



    buf = connection.recv(bufsize)
    msg = buf.decode()
    print('Client says: \'' + msg + '\'')

    connection.close()
    server.close()

if __name__ == '__main__':
    main()

helloclient.py

import socket

def main():
    serveripaddr = '127.0.0.1'
    serverport = 50001
    bufsize = 1024

    client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, \
            proto=0, fileno=None)
    serveraddr = (serveripaddr, serverport)
    client.connect(serveraddr)

    buf = client.recv(bufsize)
    msg = buf.decode()
    print('Server says: \'' + msg + '\'')

    msg = 'Hey, I am the client. Not much, server.' 
    buf = msg.encode()
    client.send(buf)

    client.close()

if __name__ == '__main__':
    main()

Exercise 1. TCP Socket and Packet Capture

Now complete the following tasks,

  1. Run the applications on two hosts, and capture the packets generated by the application using ScaPy.

Interfacing with UDP

UDP provides a datagram service for applications on the Internet. The only addition to IP offered by UDP is to allow applications to share a single network layer service, i.e., it offers a simple multiplexing service over IP. As such, the datagram service is a best effort service, and it makes an attempt to transmit a datagram and nothing more. There is no guarantee that the datagram will arrive at the destination, and nor does it know. This is a shortcoming, and also an advantage. For instance, we can efficiently broadcast or multicast datagrams to multiple destinations leveraging on the fact that LANs are generally broadcasting in nature.

Although you can still have the notion of the server and the client when you build an application, the notions of the server and the client are different in a UDP application from those in a TCP application. In an UDP application, the client is responsible for receiving the datagrams and must run the first while the server is responsible for sending for the datagrams and must run the second. To avoid confusion, we would probably better off calling them the sender and the receiver respectively. Just like a TCP application, a single program can have both of the server and the client logic if we design it in such a way.

The Hello Announcer Application (UDP Broadcasting)

Let’s consider an announcer application. This application is to make an announcement all hosts on a network. Contrasting to a possible solution using TCP, we can build an application using UDP and this application uses the network bandwidth more efficiently than that using TCP because the application broadcast the announcement in a datagram to all of the hosts on the network

helloannouncer.py

import socket

def main():
    brdipaddr ='192.168.56.255'
    udpport = 50001

    sender = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM, \
            proto=socket.IPPROTO_UDP, fileno=None)
    sender.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    brdaddr = (brdipaddr, udpport)
    msg = 'The registrar has released the final exam schedule.'
    buf = msg.encode()
    sender.sendto(buf, brdaddr)

    sender.close()

if __name__=='__main__':
    main()

helloreciever.py

import socket

def main():
    brdipaddr = '192.168.56.255'
    udpport = 50001
    bufsize = 1024

    receiver = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM, \
            proto=0, fileno=None)

    brdaddr = (brdipaddr, udpport)
    receiver.bind(brdaddr)

    buf, senderaddr = receiver.recvfrom(bufsize)
    msg = buf.decode()
    print('Received announcement from ' + str(senderaddr) + ': ' + msg)

    receiver.close()


if __name__=='__main__':
    main()

Exercise 2. UDP Socket and Packet Capture

Now complete the following tasks,

  1. Run the applications on three hosts (why three hosts?), and capture the packets generated by the application using ScaPy.
  2. We can use UDP to have one-on-one communication, i.e., a unicast communcation between two hosts. Can you rewrite the helloannouncer.py and helloreceiver.py into a unicast UDP application?