Build TCP, UDP client and TCP server written in Python

Build TCP, UDP client and TCP server written in Python

TCP (Transmission Control Protocol):

  • TCP provides a reliable, connection-oriented communication stream.
  • It ensures that data sent from one end is received correctly at the other end.
  • It handles issues like packet loss, out-of-order packets, and retransmission.

UDP (User Datagram Protocol):

  • UDP is a connectionless protocol that sends datagrams without establishing a connection.
  • It is faster than TCP but does not guarantee delivery or order of packets.
  • It is suitable for scenarios where low latency and real-time communication are more critical than reliability.

TCP is suitable for applications that require a reliable and ordered data stream, while UDP is suitable for real-time applications where speed is more crucial than guaranteed delivery.

TCP Client

Python
import socket

target_host = "www.google.com"
target_port = 80

# create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# connect the client
client.connect((target_host, target_port))

# send some data
client.send(b"GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")

# receive data
response = client.recv(4096)

client.close()

print(response)

Explanation:

  1. Setting Target Host and Port:

    • target_host is set to “www.google.com”, which is the domain of the target server.
    • target_port is set to 80, which is the standard port for HTTP.
  2. Creating a Socket:

    • socket.socket(socket.AF_INET, socket.SOCK_STREAM) creates a socket object.
    • AF_INET specifies the address family as IPv4, and SOCK_STREAM specifies a TCP socket.
  3. Connecting to the Server:

    • client.connect((target_host, target_port)) establishes a connection to the specified target host and port.
  4. Sending an HTTP GET Request:

    • client.send(b"GET / HTTP/1.1\r\nHost: google.com\r\n\r\n") sends an HTTP GET request to the server.
    • The request is composed of the HTTP method (“GET”), the requested resource (“/”), and the HTTP version (“HTTP/1.1”).
    • The “Host” header indicates the host for which the request is intended.
  5. Receiving Data from the Server:

    • client.recv(4096) receives up to 4096 bytes of data from the server in response to the HTTP request.
    • This data is typically the HTML content of the home page.
  6. Closing the Connection:

    • client.close() closes the socket connection to the server.
  7. Printing the Response:

    • print(response) prints the received response, which typically contains the HTML content of the Google home page.

UDP Client

Python
import socket

target_host = "127.0.0.1"
target_port = 80

# create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# send some data
client.sendto(b"AAABBBCCC", (target_host, target_port))

# receive some data
data, addr = client.recvfrom(4096)

client.close()

print(data)

It sends a UDP datagram to a target server at the specified IP address and port and then waits to receive a response.

Explanation:

  1. Setting Target Host and Port:

    • target_host is set to “127.0.0.1”, which is the loopback address (localhost).
    • target_port is set to 80, which is an arbitrary port number.
  2. Creating a UDP Socket:

    • socket.socket(socket.AF_INET, socket.SOCK_DGRAM) creates a socket object for UDP communication.
    • AF_INET specifies the address family as IPv4, and SOCK_DGRAM specifies a UDP socket.
  3. Sending a UDP Datagram:

    • client.sendto(b"AAABBBCCC", (target_host, target_port)) sends a UDP datagram containing the bytes “AAABBBCCC” to the specified target host and port.
    • Unlike TCP, UDP is a connectionless protocol, so there is no need to establish a connection before sending data.
  4. Receiving Data from the Server:

    • client.recvfrom(4096) receives up to 4096 bytes of data from the server.
    • The received data is stored in the data variable, and the address of the sender is stored in the addr variable.
  5. Closing the UDP Socket:

    • client.close() closes the UDP socket. Unlike TCP, there is no need to explicitly close connections in UDP since it is connectionless.
  6. Printing the Received Data:

    • print(data) prints the received data, which, in this case, is the response from the server.

The basic principles of UDP communication: sending datagrams without establishing a connection and receiving datagrams from a specified target. Note that UDP does not guarantee the order of delivery or that the datagram will reach its destination, so additional mechanisms may be needed for reliability if required for a specific application.

TCP Server

Python
import socket
import threading

bind_ip = "0.0.0.0"
bind_port = 9999

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.bind((bind_ip, bind_port))

server.listen(5)

print("[*] Listening on %s:%d" % (bind_ip, bind_port))


# this is our client handling thread
def handle_client(client_socket):
    # just print out what the client sends
    request = client_socket.recv(1024)

    print("[*] Received: %s" % request)

    # send back a packet
    client_socket.send(b"ACK!")
    print(client_socket.getpeername())
    client_socket.close()


while True:
    client, addr = server.accept()

    print("[*] Accepted connection from: %s:%d" % (addr[0], addr[1]))

    # spin up our client thread to handle incoming data
    client_handler = threading.Thread(target=handle_client, args=(client,))
    client_handler.start()

The server listens for incoming connections on a specified IP address and port, and for each connection, it creates a new thread to handle the communication with the connected client.

Explanation:

  1. Socket Creation and Binding:

    • The server creates a TCP socket using socket.socket(socket.AF_INET, socket.SOCK_STREAM).
    • The socket is bound to the specified IP address (0.0.0.0, which means it listens on all available interfaces) and port (9999) using server.bind((bind_ip, bind_port)).
  2. Listening for Incoming Connections:

    • The server listens for incoming connections with a backlog of 5 using server.listen(5).
  3. Handling Client Connections:

    • In an infinite loop (while True), the server accepts incoming connections with server.accept().
    • For each accepted connection, it prints a message and creates a new thread (client_handler) to handle communication with the connected client.
  4. Handling Client Data in a Thread:

    • The handle_client function is responsible for handling communication with a connected client.
    • It receives data from the client using client_socket.recv(1024).
    • It prints the received data and sends a simple acknowledgment back to the client using client_socket.send(b"ACK!").
    • It prints the peer’s address (client’s address) using client_socket.getpeername().
    • It closes the client socket.
  5. Thread Creation:

    • Each time a connection is accepted, a new thread (client_handler) is created to handle that specific client using threading.Thread(target=handle_client, args=(client,)).
    • The thread is started with client_handler.start().

This server is a simple demonstration and lacks error handling and additional features that might be necessary in a production environment. The use of threading allows the server to handle multiple clients concurrently. Keep in mind that for production use, more sophisticated mechanisms like a thread pool or an asynchronous approach may be considered.

Total
6
Shares

Leave a Reply

Previous Post
Generating keys securely with the secrets and hashlib modules

Generating keys securely with the secrets and hashlib modules

Next Post
A simple network client and server to push files and command-line access

A simple network client and server to push files and command-line access

Related Posts