Network Programming


Sockets

  • We have talked about varoius ways of getting networking. One way all of us are familiar are is HTTP, even FTP. Infact even bluetooth is a way of networking.

  • At the heart of networking is sockets. They form the lowest level of networking.


In [ ]:
import socket 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# This creates a socket

# AF_INET => family ipv4. 
# SOCK_STREAM => TCP protocol.
  • Sockets are the endpoints of a bidirectional communications channel.
  • Sockets can be used to communicate within a process, between processes on the same machine, or even between processes on different computers.

  • One socket(node) listens on a particular port at an IP, while other socket reaches out to the other to form a connection.

  • Server forms the listener socket while client reaches out to the server.

  • TCP protocol(Transmission Control Protocol ):
    • TCP is a connection-oriented, UDP isnt.
    • TCP is highly reliable, as in a packet is lost in trasnmission, it is resent. Also gaurentees the order of packets
    • TCP is slow and UDP is fast

We will first try to make a simple TCP server and client


In [ ]:
import socket 


def client_handler(client_sock):
    # Do things here

    # .send() Takes byte type object
    # b' it indicates that the literal should become a bytes literal in Python 3
    client_sock.send(b'Thank you for connecting') 

    # Close the connection with the client
    client_sock.close()


# creates the socket
server = socket.socket()

PORT = 1234

# bind the socket to the port. 
server.bind(('localhost',PORT))   # takes a tuple

# at most 5 connections in the queue
server.listen(5)   

print("Listening for clients to connect")

while True:
 
    # Establish connection with client.
    client, addr = server.accept()     # waits until a connection arrives i.e (blocking).
    print('Got connection from', addr)
    client_handler(client)
  • To check if the socket is actually created one can check using the command netstat -nlp on the command line.

In [ ]:
from socket import *

PORT = 1234
client = socket()

address=("localhost", PORT)

# connect to the given address
client.connect(address)

print(client.recv(1024))

client.close()
  • Blocking Execution : These functions/constructs stop main execution until its own execution ends. Also called synchronous.
  • Non-Blocking : These functions return immediately and continue execution sperately. Also called asynchronous.
    • Can be obtained by the use of async/await
    • Or by using threads.

Threads

  • Threads are constructs that enable parallel (actually concurrent) execution of codes.
  • Thread class is available in the threading module.
  • One has to override the run() method of Thread class to inherit it.

An example:


In [5]:
from threading import *

def lift_off(number):
    for i in range(3,0, -1):
        print("#"+ str(i) + "("+ str(number) + ") ")

for x in range(7):
    Thread(target=lift_off, args=(x,)).start()


#3(0) 
#3(1) #2(0) 

#2(1) #1(0) 

#1(1) 
#3(2) 
#2(2) 
#1(2) 
#3(3) #3(4) 
#2(3) #3(5) 


#2(5) #2(4) 

#1(5) #1(4) 

#1(3) #3(6) 
#2(6) 
#1(6) 

So the threads execute concurrently independent of each other.


In [4]:
# Inheriting from the Thread class

from threading import *

class lift_off(Thread):
    def __init__(self, number):
        Thread.__init__(self)
        self.number = number
    def run(self):
        for i in range(3,0, -1):
            print("#"+ str(i) + "("+ str(self.number) + ") ")

for x in range(7):
    lift_off(5).start()


#3(5) 
#2(5) 
#1(5) 
#3(5) 
#2(5) #3(5) #3(5) #3(5) #3(5) #3(5) 





#1(5) #2(5) #2(5) #2(5) #2(5) #2(5) 





#1(5) #1(5) #1(5) #1(5) 



#1(5) 

Lets make a small chat application


In [ ]: