Python: Sockets Programming - Blocking Client


Sockets are an operation system construct allowing Inter-Process Communication (IPC) over a network. Sockets is how you are able to browse the internet, watch movies or download files from your computer.

The socket module in Python allows us to create a socket connection to a remote server or create a listening socket acting as a server. Under the hood, the socket module uses operation system calls to communicate with sockets, hence there are some small differences in using sockets in Windows and UNIX/Linux based systems.

In Python, socket calls to send or receive data can be blocking or non-blocking. Blocking calls wait and do not go to the next line of execution till the required data is full or the socket is closed. Non-blocking calls are asynchronous operations that can execute the next line of code even if data is coming as they call a separate function to handle the sending/receiving of data. In this article the client we only uses blocking socket calls, and we have shown non-blocking call in another article.

In the following code we show how we write a basic socket client. This socket client connects to a website (http://www.dasmic.com) and sends a 'GET' HTTP request. This socket client has similar functionality to the popular program 'telnet' and the following image shows the output from telnet (in Windows 10) when the same request to port 80 is made, by entering the command 'telnet www.dasmic.com 80' in the command prompt.


Communication through sockets is an I/O operation hence as we learned in a previous article, it is good practice to run I/O operation in a separate thread. In the code the tcpSocketThreadClassBlocking inherits from the threading.thread class and is run in a separate thread as we learned in this article. The demoSocketClass class instantiates tcpSocketThreadClassBlocking and starts the threads, while main() is used as the entry code that instantiates demoSocketClass. Inline comments in the code further explain the purpose of important functions.


import threading
import socket

# Set the buffer size to receive data
# It is recommended that this be a power of 4
SIZE_BUFFER = 4096

# The run() function in this class will be executed in its own thread
class tcpSocketThreadClassBlocking(threading.Thread):
    def __init__(self, host, port):
        threading.Thread.__init__(self)
        self.host = host
        self.port = port

    def run(self):
        print("--------- in socket thread.run()")
        dataTX = bytes('GET /\n', 'utf-8')

        # None is equivalent to null
        # dataRX will be a bytes list
        dataRX = None
        tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Set the socket to blocking
        # Setting tcpSock.settimeout(none) will also have the same effect
        # as tcpSock.setblocking(1)
        tcpSock.setblocking(1)

        try:
            print("Connecting to socket ")
            tcpSock.connect((self.host, self.port))
        except:
            print("Could not connect to server")

        try:
            # sendall() will send all data in the buffer
            # internally, it uses the send() function
            tcpSock.sendall(dataTX)
            # Now receive data upto a maximum of BUFFER_SIZE at a time
            # Note: This is a blocking call and will receive data till buffer is full.
            # However, if the remote socket closes the connection then it will exit out
            # of the blocking call
            dataRX = tcpSock.recv(SIZE_BUFFER)
            if len(dataRX) != 0:
                # Convert bytes list into a string
                dataRXStr = dataRX.decode("utf-8")
                # Print the string
                print("Data received from server:", dataRXStr)
            else:
                print("Remote server closes connection and did:", dataRX)

        except Exception as e:
            print("Error in receiving data. Error:",str(e))
            print("Data received (if any)", dataRX)
        # Close the socket
        try:
            # It is good practise to explicitly shutdown the socket before closing it
            # with the SHUT_RD option any further ends to this socket are disallowed
            # other options are SHUT_WR and SHUT_RDWR
            tcpSock.shutdown(socket.SHUT_RD)
            # Now close the socket for proper cleanup
            tcpSock.close()
            print("Successfully closed socket ")
        except:
            print("Could not close socket")

# Class to instantiate the threading class and run the thread
class demoSocketClass:
    def runSocketThread(self):
        print("----------- runSocketThread")
        # Connect to the HTTP (port 80) of server www.dasmic.com
        # You can also specify an IP address here
        socketThread = tcpSocketThreadClassBlocking("www.dasmic.com", 80)
        print("Starting socket thread")
        socketThread.start()
        print("Socket thread started")

# main() function which contain the high level routines
def main():
    dsc = demoSocketClass()
    dsc.runSocketThread()

# Call the main() function
main()

The output by calling the main() function is given below. Note how the printed value of the dataRXStr is similar to the output from telnet shown above. This is because we are connected to the same web server and send the same 'GET /' request.

----------- runSocketThread
Starting socket thread
--------- in socket thread.run()
Socket thread started
Connecting to socket 
Data received from server: 



403 - Forbidden: Access is denied.



403 - Forbidden: Access is denied.

You do not have permission to view this directory or page using the credentials that you supplied.

Successfully closed socket Process finished with exit code 0

Comments

Popular posts from this blog

Part III: Backpropagation mechanics for a Convolutional Neural Network

Introducing Convolution Neural Networks with a simple architecture

Deriving Pythagoras' theorem using Machine Learning