Build a TCP/UDP Client and Server to Send and Receive Packets

Posted by Aaron John on March 3, 2019

In this documentation, I will go through the process on how to write a TCP (Transmission Control Protocol) and UDP (User Datagram Protocol) Client and Server program which can send and receive packets of information.

Both TCP and UDP have a point to point architecture, and it uses sockets for client to communicate with the server and vice-versa. When it comes to understanding socket programming, there are two socket types for two transport services:

  • which is datagram (which is unreliable) for UDP
  • and byte stream-oriented (which is reliable) for TCP

An application example would be the below code. Here, I wrote a TCP client and server program in Python where the client gets a set of integers and the length of the set as command line arguments. Then the client sends this set to the server. Afterwards, the server computes the total, the highest number, the lowest number, and the mean of this set and sends the results to the client. Finally, the client receives the results and prints them out.

In the process of sending and receiving messages there must be an identifier. When it comes to addressing processes we use the identifier in a socket, which includes both IP address and port number associated with process on host. (Fun Fact about Transport Layer: the Transport Layer is defined as both connection-oriented and connectionless. Where TCP is connection-oriented and UDP is connectionless.)

Advantages of TCP

  1. Reliable Transport between sending and receiving process. Error detection. When a TCP connection breaks (due to the server being unavailable) the client stops sending data and restarts the connection process once the server becomes available. With UDP, since it’s connection-less the client continues to pound the network with data until the server reestablishes a connection.

  2. There is Flow control, hence sender will not overwhelm receiver. Performance gain on heavily loaded networks because TCP acknowledges every packet, unlike UDP which only acknowledges completion.

  3. There is Congestion control where TCP will throttle sender when network overloads. TCP has better congestion control than UDP (which has none). On a very congested network, UDP packets are the first packets that are dropped. This means that if NFS is writing data (in 8K chunks) all of that 8K must be retransmitted over UDP. Because of TCP’s reliability, only parts of that 8K data are transmitted at a time.

  4. TCP is connection oriented, that is why there is an accept in the server code. For TCP, a handshake is required between the sender and receiver before exchanging any data. Improved connection durability, thus less NFS stale file handles messages.

TCP Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    import socket

    serverIP = 'localhost'
    serverPort = 16000

    clientSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    clientSock.connect((serverIP, serverPort))

    message = raw_input("Input integers with space in between: ")
    message2 = raw_input("Enter the length of the set: ")

    clientSock.send(message)
    clientSock.send(message2)

    data = clientSock.recv(1024)

    temp = [float(x) for x in data.split(' ')]

    print("The total of all numbers is: " + str(temp[0]))
    print("The lowest number is: " + str(temp[1]))
    print("The highest number is: " + str(temp[2]))
    print("The mean is: " + str(temp[3]))

    clientSock.close()

TCP Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    import socket

    serverIP = 'localhost'
    serverPort = 16000

    serverSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverSock.bind((serverIP, serverPort))
    serverSock.listen(1)

    print("TCP server has started and is ready to receive")

    while 1:
        connection, addr = serverSock.accept()
        data = connection.recv(1024)

        if not data: break

        temp = [float(x) for x in data.split(' ')]
        print "Received data:", temp

        length = len(temp)
        maximum = max(temp)
        minimum = min(temp)
        total = sum(temp)
        mean = total/length

        msg = str(total) + " " + str(minimum) + " " + str(maximum) + " " + str(mean)

        connection.send(str(msg))

Use of UDP

  • When it comes to UDP, there is unreliable data transfer between sending and receiving process. Also, it does not provide reliability, flow control, congestion control, timing, throughput guarantee or security. UDP is connectionless between client and server, therefore, there is no handshaking before sending data. Therefore data can be sent freely. Here, sender explicitly attaches IP destination address and port number to each packet. Next, the receiver extracts sender’s IP address and port number from received packets. (Note: transmitted data may be lost or received out of order)

UDP Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    import socket

    serverIP = 'localhost'
    serverPort = 15000

    clientSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    clientSock.bind((serverIP, 8000))

    message = raw_input("Input integers with space in between: ")
    message2 = raw_input("Enter the length of the set: ")

    clientSock.sendto(message, (serverIP, serverPort))
    clientSock.sendto(message2, (serverIP, serverPort))

    data, addr = clientSock.recvfrom(2048)

    temp = [float(x) for x in data.split(' ')]

    print("The total of all numbers is: " + str(temp[0]))
    print("The lowest number is: " + str(temp[1]))
    print("The highest number is: " + str(temp[2]))
    print("The mean is: " + str(temp[3]))

    clientSock.close()

UDP Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    import socket

    serverIP = "localhost"
    serverPort = 15000

    serverSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    serverSock.bind((serverIP, serverPort))

    print("UDP server has started and is ready to receive")

    while True:
        data, addr = serverSock.recvfrom(2048)
        temp = [float(x) for x in data.split(' ')]
        print "received data:", temp

        length = len(temp)
        maximum = max(temp)
        minimum = min(temp)
        total = sum(temp)
        mean = total/length

        msg = str(total) + " " + str(minimum) + " " + str(maximum) + " " + str(mean)

        serverSock.sendto(str(msg), (serverIP, 8000))

How to Run the Above Code

  1. First, we will start off by opening a new terminal window.

  2. Next, start off by changing the current directory to the directory where you saved your client and server .py files by using the “cd” command.

  3. Once you have “cd” into the directory (where you saved the client server .py files) in terminal, lets start off by testing the UDP protocol by running the serverUDP.py file as follows:
    1
    
    python serverUDP.py
    
  4. Next, lets open another terminal window and “cd” into the directory where the .py files are saved. Here, we will run the clientUDP.py file which will then cause the client file to make a connection with the serverUDP.py file.
    1
    
    python clientUDP.py
    
  5. After running the clientUDP.py file, it will prompt the user to input integers with a space between each integer. Hit enter once you have input the desired amount of integers. (Note: Feel free to enter only just one integer.)

  6. Again the clientUDP.py file will prompt the user to enter the length of the above set. Hence, enter the integer length of the set when prompted.

  7. Repeat steps 3, 4, 5 and 6 using the files serverTCP.py and clientTCP.py to test out the TCP protocol.

Test Case for UDP Protocol As mentioned above you will run the serverUDP.py file first in a terminal window, and in another separate terminal window you will run the clientUDP.py file so it can establish a connection with the server file.

  • Below is an example input for inputting a set of integers from clientUDP side:
    1
    
    Input integers with space in between: 6 5 9 7 4.2
    
  • Next, the user will enter the length of the above set when prompted from clientUDP side:
    1
    
    Enter the length of the set: 5
    
  • Next, you will notice a screen output on the serverUDP side as follows:
    1
    2
    3
    
    UDP server has started and is ready to receive
    received data: [6.0, 5.0, 9.0, 7.0, 4.2]
    received data: [5.0]
    
  • Finally you will should see an output on the clientUDP side as follows:
    1
    2
    3
    4
    
    The total of all numbers is: 31.2
    The lowest number is: 4.2
    The highest number is: 9.0
    The mean is: 6.24
    

Test Case for TCP Protocol As mentioned above you will run the serverTCP.py file first in a terminal window, and in another separate terminal window you will run the clientTCP.py file so it can establish a connection with the server file.

  • Below is an example input from clientTCP side:
    1
    
    Input integers with space in between: 5 4 6 9 3.4
    
  • Next, the user will enter the length of the above set when prompted from clientUDP side:
    1
    
    Enter the length of the set: 5
    
  • Next, you will notice a screen output on the serverTCP side as follows:
    1
    2
    
    TCP server has started and is ready to receive
    Received data: [5.0, 4.0, 6.0, 9.0, 3.4]
    
  • Finally you will should see an output on the clientTCP side as follows:
    1
    2
    3
    4
    
    The total of all numbers is: 27.4
    The lowest number is: 3.4
    The highest number is: 9.0
    The mean is: 5.48