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. Server Error
Successfully closed socket Process finished with exit code 0
Comments
Post a Comment