Socket programming in Python

svg viewer

Introduction

Sockets are endpoints in bi-directional communications between two processes.

Sockets allow us to connect, send, and receive messages across a network. The network can be logical, local, or external. A socket connects and then uses the read() and write() commands the same way that file-descriptors use files and pipes.

Python has a library for socket programming that provides an interface to the Berkeley sockets API. You can import the socket library by writing:

import socket

Some fundamental python socket API methods are:

  • socket()
  • bind()
  • listen()
  • accept()
  • connect()
  • send()
  • recv()
  • close()

TCP and UDP sockets

Before you can use any other socket function, you need to create a socket. To create a socket, we use the socket() method. It is up to the user to create either a TCP socket or a UDP socket.

Compared to a UDP socket, a TCP socket is more reliable and provides in-order data delivery. However, UDP sockets deliver messages faster. It is advised that you use a TCP socket to achieve best-effort delivery from the underlying network.

Creating a TCP socket:

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

Creating a UDP socket:

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

socket.AF_INET is used to designate the type of addresses our socket can communicate with which, in this case, is ipv4 addresses. socket.SOCK_STREAM specifies a TCP socket and socket.SOCK_DGRAM specifies a UDP socket.

Exploring fundamental socket programming methods

Now, ​we’ll use this illustration to explain the functionality of different socket programming methods.

svg viewer
  • Both the server and client first create a socket to communicate with other processes. To create a socket, we use the socket() method.

  • Server needs to bind its socket to a specific port and IP. This port and IP will be used by the server to send and receive messages. Use the bind() method to bind the socket.

  • After binding to a defined port and IP, the server needs to listen for incoming connections or messages at this port and IP. Use the listen() method to listen through the socket.

  • For a client to communicate with the server, it first needs to connect to it. Use the connect() method to connect to a port and IP.

  • Before proceeding with the exchange of messages, the server needs to accept the request of a client to connect. Use the accept() method to accept a connection.

  • After the connection is established, both the server and client can exchange messages. U se the send() and recv() method to exchange messages.

  • Both server and client can close a socket and terminate the connection. Use the close() method to close the socket.

To learn more about socket programming in python, visit the official documentation.

Client-server example

Here we have a naive implementation of a client-server chat app using python socket programming.

client.py

client.py consists of a Client class that takes care of the client-side implementation of the chat app. There are five methods (including the constructor):

  1. __init__: Here, the server’s port and address are initialized. Moreover, the client’s socket is initialized and bound to a random port.

  2. join: This method sends the join-message to the server when the client code is run for the first time.

  3. send_message: This method sends the message entered by the client to the server – the server then forwards it to the recipient.

  4. start: This method is called at the start of the code. It calls either the join method or the send_message method depending on the input signal.

  5. receive_handler: This method receives the message from the server and prints it on the terminal. It is called using a thread object in order to run it parallel to the start method.

server.py

server.py consists of a Server class that takes care of the server-side implementation of the chat app. There are three methods (including the constructor):

  1. __init__: Here, the server’s socket is initialized and, by default, bound to address localhost and port 11000.

  2. forward_message: This method forwards the message to the recipient that server receives from the sender.

  3. start: This method is called upon at the start of the code. This method saves the port and address of the client if it receives a join signal, and forwards the message to the recipient if it receives a send_message signal from a the sender.

How to run the chat app

  • Press the blue button underneath the code written below.
  • Once you see the terminal, write the following command and press enter:
python3 server.py

You have successfully started the server.

  • Now, to connect a client to the server, open another terminal and write:
cd usercode

then write

python3 client.py -u Sherlock

You have successfully connected a client named Sherlock to the server.

Sherlock is not going to talk to himself, therefore, let’s make another client named Watson:

python3 client.py -u Watson

To send the message from the client terminal, follow this format:

msg {NameOfClient} {ActualMsg}

For example, you can run this command in the terminal of Sherlock:

msg Watson Education never ends, Watson. It is a series of lessons, with the greatest for the last.

Have fun chatting!

import socket
import sys
import random
from threading import Thread
import getopt



class Client:
    '''
    This is the main Client Class. 
    '''
    def __init__(self, username, dest, port):
        self.server_addr = dest
        self.server_port = port
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.bind(("", random.randint(10000, 40000)))
        self.name = username

    def join(self):
        join_message = "join" + " " + self.name
        self.sock.sendto(join_message.encode("utf-8"),(self.server_addr,self.server_port))
    
    def send_message(self, msg):
        actual_message = "send_message" + " " + msg
        self.sock.sendto(actual_message.encode("utf-8"),(self.server_addr,self.server_port))
    
    def start(self):
        '''
        Main Loop is here
        Start by sending the server a JOIN message.
        Waits for userinput and then process it
        '''
        self.join()

        while True:
            userinput = input()

            input_recv = userinput.split()

            if input_recv[0] == "msg":
                self.send_message(" ".join(input_recv[1:]))
            
            else:
                print("incorrect userinput format")
                continue            

    def receive_handler(self):
        '''
        Waits for a message from server and process it accordingly
        '''

        while True:
            server_message, server_addr_port = self.sock.recvfrom(4096)
            server_message = server_message.decode("utf-8")
            datalist = server_message.split()

            if datalist[0] == "forward_message":
                msg_recv_list = datalist[1:]
                msg_recv = " ".join(msg_recv_list)
                print(msg_recv) 
            
if __name__ == "__main__":
    DEST = 'localhost'
    PORT = 11000

    try:
        OPTS, ARGS = getopt.getopt(sys.argv[1:],
                                   "u:", ["user="])
    except:
        print("Wrong command entered.")
        exit(1)

    USER_NAME = None
    for o, a in OPTS:
        if o in ("-u", "--user="):
            USER_NAME = a

    if USER_NAME is None:
        print("Missing Username.")
        exit(1)

    S = Client(USER_NAME, DEST, PORT)
    try:
        # Start receiving Messages
        T = Thread(target=S.receive_handler)
        T.daemon = True
        T.start()
        # Start Client
        S.start()
    except (KeyboardInterrupt, SystemExit):
        sys.exit()
Copyright ©2024 Educative, Inc. All rights reserved