Blocking and Non-Blocking Socket I/O
In client server applications, when a client makes a request to a server, server processes the request and sends back a response. For this, both the client and the server first needs to establish a connection with one another through sockets (TCP or UDP). In the last few tutorials, we also saw, how a client can send data in form of request to the server and the server can operate on it, and then send a response back to the client.
Blocking Socket I/O
By default, TCP sockets are placed in a blocking mode. This means that the control is not returned to your program until some specific operation is complete.
For example, if you call the connect()
method, the connection blocks your program until the operation is complete. On many occasions, we don't want to keep our program waiting forever.
Taking another example, when we write a web browser client that connects to a web server, we should consider a stop functionality that can cancel an active connection process in the middle of its operation. This can be achieved by placing the socket in the non-blocking mode.
Non-Blocking Socket I/O
We can call setblocking(1)
to set up blocking or setblocking(0)
to unset blocking. Let's understand it with the help of an example. First of all, let's consider a Blocking Socket:
block_client.py
#!usr/bin/python
import socket
sock = socket.socket()
host = socket.gethostname()
sock.connect((host, 12345))
sock.setblocking(1)
# Or simply omit this line as by default TCP sockets
# are in blocking mode
data = "Hello Python\n" *10*1024*1024 # Huge amount of data to be sent
assert sock.send(data) # Send data till true
block_server.py
#!usr/bin/python
#block_server.py
import socket
s = socket.socket()
host = socket.gethostname()
port = 12345
s.bind((host,port))
s.listen(5)
while True:
conn, addr = s.accept() # accept the connection
data = conn.recv(1024)
while data: # till data is coming
print data
data = conn.recv(1024)
print "All Data Received" # Will execute when all data is received
conn.close()
break
Now, run block_server.py
first and then block_client.py
. You'll notice that the server keeps on printing Hello Python. This will go on and on till all the data is sent. In the above code the line All Data Received will not be printed for long time, because as the client has to send a large number of string, which will take time, and until then the socket input-output will get blocked.
What's going on here? The send()
method will try to transmit all the data to the server, while the write buffer will get filled up. The kernel will put the process to sleep until the data in the buffer is transferred to the destination and the buffer is empty again. When the buffer becomes empty, the kernel will wake the process up again to get the next chunk of data that is to be transferred. In short, your code will block and it will not let anything else proceed.
Now consider a Non-Blocking Socket
#!usr/bin/python
# non_blocking_client.py
import socket
sock = socket.socket()
host = socket.gethostname()
sock.connect((host, 12345))
sock.setblocking(0) # Now setting to non-blocking mode
data = "Hello Python\n" *10*1024*1024 # Huge amount of data to be sent
assert sock.send(data) # Send data till true
Now, if we run the non_blocking_client.py
, you'll notice that the program will run for a small time, it will print the last line "All Data Received" and soon terminate.
What's going on here? Here the client did not send all the data. When we make a socket non-blocking by calling setblocking(0)
, it will never wait for the operation to complete. So when we call the send()
method, it will put as much data in the buffer as possible and return.