Network Programming: Multi-Threaded Synchronization PDF

Summary

This document discusses network programming focusing on multi-threaded synchronization techniques. It explores topics such as socket programming with UDP and TCP, single-threaded vs multi-threaded servers. The document also provides practical examples and testing scenarios, aiming to illustrate concepts with code samples.

Full Transcript

Network Programming: Multi‐ Threaded Synchronization Dr. Ala Altaweel University of Sharjah Department of Computer Engineering [email protected] 1 Application Layer: Overview  Principles of network applica...

Network Programming: Multi‐ Threaded Synchronization Dr. Ala Altaweel University of Sharjah Department of Computer Engineering [email protected] 1 Application Layer: Overview  Principles of network applications  Socket programming with UDP and TCP  Single‐Threaded v.s. Muti‐ Threaded Multi‐Threaded Server Multi‐Threaded Synchronization Multi‐Threaded Chat Server  Web and HTTP  The Domain Name System DNS Multi‐Threading Synchronization: 2‐2 An enhancement to the Multi‐Threaded server Current version of our Multi‐Threaded server store only one copy of clients’ sent files We name this file as, from_client But what if we want to store all sent files from clients Let’s modify our Multi‐Threaded server such that the server append all sent files from clients into one file named all_received How can we achieve that with many threads for clients? Global variable! Multi‐Threading Synchronization: 2‐3 Multi‐Threaded TCP Server to append all received files Python TCPServer import socket import sys import os.path import operator import thread same code as for serverPort = 7005 single- #create socket object for server threaded serverSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server #socket is bound to localhost and port 7005 serverSocket.bind(('',serverPort)) #accept up to 2 incoming connections serverSocket.listen(2) #server start to listen on localhost and port print ('Server listening...') Multi‐Threading Synchronization : 2‐4 Multi‐Threaded TCP Server to append all received files Python TCPServer same code #get the host name of the server as for print ("Server machine name:", socket.gethostname()) single- #get the ip address of the host name threaded print ("Server IP address: ", socket.gethostbyname(socket.gethostname())) server print ("Server Port #: ", serverPort) #a file, named all_received, to receive from all clients written_file = open("all_received", "ab") define a global file to be accessed by all threads Multi‐Threading Synchronization : 2‐5 Multi‐Threaded TCP Server to append all received files (only the code for handling SEND command) Python TCPServer same code #server receives SEND command from client as for #server should create a file to be received by the client single- elif request_str == 'SEND': threaded print('Server received SEND command from client. Waitng for filename...') server #append into written_file #check if written_file closed, open it global written_file access the global file check if file is closed if written_file.closed: print("all_received file is closed, reopen it") written_file = open("all_received", "ab") Multi‐Threading Synchronization : 2‐6 Multi‐Threaded TCP Server to append all received files (only the code for handling SEND command) Python TCPServer print('Receiving file..') l = connectionSocket.recv(1024) while(l): written_file.write(l) same l = connectionSocket.recv(1024) concept as for close the file written_file.close() single- print('Done with receiving file from client’) threaded connectionSocket.close() server except Exception as e: print("error occured in handleRequestThread function: ", e) Multi‐Threading Synchronization : 2‐7 Testing the Multi‐Threaded Server to append all received files Let’s try the below scenario on Multi‐Threaded server Scenario1: 1st client connected, 1st client issue a SEND request for file1 and send it 1st client issue a SEND request for file2 and send it 2nd client connected, 2nd client issue a SEND request for file1 and send it 2nd client issue a SEND request for file2 and send it How will the Multi‐Threaded server handle the clients request? The files will be appended in order Multi‐Threading Synchronization: 2‐8 Testing the Multi‐Threaded Server to append all received files Let’s try the below scenario on Multi‐Threaded server Scenario2: 1st client connected, 1st client issue a SEND request for file1 and send it 2nd client connected, 2nd client issue a SEND request for file1 and send it 1st client connected, 1st client issue a SEND request for file2 and send it 2nd client connected, 2nd client issue a SEND request for file2 and send it How will the Multi‐Threaded server handle the clients request? The files will be appended in order Multi‐Threading Synchronization: 2‐9 Testing the Multi‐Threaded Server to append all received files Let’s try the below scenario on Multi‐Threaded server Scenario3: 1st client connected, 1st client issue a SEND request for file1 2nd client connected, 2nd client issue a SEND request for file1 and send it 1st client send file1 How will the Multi‐Threaded server handle the clients request? Error Exception raised: ValueError(“I/O operation on closed file”) Multi‐Threading Synchronization: 2‐10 What are the problems of Multi‐Threaded Server? Once a client issues a SEND request, it should send the file before the second client (i.e., or any other client): Issue a SEND request and send any file This is a synchronization problem multi‐writers on shared data reader‐writer (producer‐consumer) problem Multi‐Threading Synchronization: 2‐11 How to synchronize threads? How to guarantee only one thread at a time accessing the shared data in Python? lock objects from threading module have the following methods: lock.acquire() Acquires the lock unconditionally, if necessary waiting until it is released by another thread lock.release() Releases the lock lock must have been acquired earlier, but not necessarily by the same thread Multi‐Threading Synchronization: 2‐12 Multi‐Threaded TCP Server to append all received files – with synchronization Python TCPServer import socket import sys import os.path import operator import thread import threading threading module to use lock same code as for serverPort = 7005 single- threaded #create socket object for server server serverSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #socket is bound to localhost and port 7005 serverSocket.bind(('',serverPort)) #accept up to 2 incoming connections serverSocket.listen(2) #server start to listen on localhost and port Multi‐Threading Synchronization : 2‐13 print ('Server listening...') Multi‐Threaded TCP Server to append all received files ‐ with synchronization Python TCPServer same code #get the host name of the server as for print ("Server machine name:", socket.gethostname()) single- #get the ip address of the host name threaded print ("Server IP address: ", socket.gethostbyname(socket.gethostname())) server print ("Server Port #: ", serverPort) #a file, named all_received, to receive from all clients written_file = open("all_received", "ab") define a global file to be accessed by all threads threadLock = threading.Lock() define a global lock to synchronize all threads Multi‐Threading Synchronization : 2‐14 Multi‐Threaded TCP Server to append all received files (only the code for handling SEND command) ‐ with synchronization Python TCPServer same code #server receives SEND command from client as for #server should create a file to be received by the client single- elif request_str == 'SEND': threaded print('Server received SEND command from client. Waitng for filename...') server #acquire the lock global threadLock access the global lock print("threadLock is acquired by: ", thread.get_ident()) get thread id and print it threadLock.acquire() acquire the global lock, this is a blocking call print("threadLock is given to: ", thread.get_ident()) reaching here indicates lock is given and booked by this thread #append into written_file #check if written_file closed, open it global written_file access the global file check if file is closed if written_file.closed: print("all_received file is closed, reopen it") written_file = open("all_received", "ab") Multi‐Threading Synchronization : 2‐15 Multi‐Threaded TCP Server to append all received files (only the code for handling SEND command) ‐ with synchronization Python TCPServer print('Receiving file..') l = connectionSocket.recv(1024) same code as for while(l): single- written_file.write(l) threaded l = connectionSocket.recv(1024) server close the file written_file.close() #release the lock release the lock, let threadLock.release() others access print("threadLock is released by: ", thread.get_ident()) reaching here indicates lock print('Done with receiving file from client') is released by this thread connectionSocket.close() except Exception as e: print("error occured in handleRequestThread function: ", e) Multi‐Threading Synchronization : 2‐16 Testing the Multi‐Threaded Server to append all received files Let’s retry the same scenario that we did before for the Multi‐ Threaded server Scenario3: 1st client connected, 1st client issue a SEND request for file1 2nd client connected, 2nd client issue a SEND request for file1 and send it 1st client send file1 How will the Multi‐Threaded server handle the clients request? The clients’ SEND requests will be synchronized and no I/O Error will occur!! This handle the case of real‐life scenarios Multi‐Threading Synchronization: 2‐17 Application Layer: Overview  Principles of network applications  Socket programming with UDP and TCP  Single‐Threaded v.s. Muti‐ Threaded Multi‐Threaded Server Multi‐Threaded Synchronization Multi‐Threaded Chat Server  Web and HTTP  The Domain Name System DNS Multi‐Threading Synchronization: 2‐18 Chat Room of Server and Clients We aim to design and implement a simple Chat Room that enable users to send messages to each others We will follow the Client‐Server model This is common in many chat applications: What’s App groups Exam rooms we use on Microsoft Teams etc. Multi‐Threading Synchronization: 2‐19 Chat Room of Server and Clients Which transport layer protocol we should use in our Chat Room? TCP UDP Since we are following the Client‐Server model Clients will communicate with the server Server will handle all communication amongst clients Multi‐Threading Synchronization: 2‐20 Chat Room of Server and Clients: Clients Join Chat Client‐1 ? Chat Server 1. Let Client‐1 know that Client‐2 joined Chat Client‐2 Multi‐Threading Synchronization: 2‐21 Chat Room of Server and Clients: Clients Join Chat Client‐1 ? Chat Client‐3 Chat Server 1. Let Client‐1 know that Client‐2 joined 2. Let Client‐1 & Client‐2 know that Client‐3 joined Chat Client‐2 Multi‐Threading Synchronization: 2‐22 Chat Room of Server and Clients: Clients Joining Chat Client‐1 ? Chat Client‐3 Chat Server 1. Let Client‐1 know that Client‐2 joined 2. Let Client‐1 & Client‐2 know that Client‐3 joined 3. Let Client‐1, Client‐2, and Client‐3 know Chat Client‐2 that Client‐4 joined Chat Client‐4 Multi‐Threading Synchronization: 2‐23 Chat Room of Server and Clients: Clients sending/receiving messages Chat Client‐1 ? Chat Client‐3 Chat Server 1. Send Client‐1 message “Hi all” to all other clients (Client‐2, Client‐3, Client‐4) Chat Client‐2 Chat Client‐4 Multi‐Threading Synchronization: 2‐24 Chat Room of Server and Clients: Clients leaving Chat Client‐1 ? Chat Client‐3 Chat Server 1. Let Client‐2, Client‐3, and Client‐4 know that Client‐1 left Chat Client‐2 Chat Client‐4 Multi‐Threading Synchronization: 2‐25 Chat Room of Server and Clients: Server Tasks Chat Client‐1 ? Chat Client‐3 Chat Server 1. Keep track of connected clients Chat Client‐2 list of clients (add when joins, remove when Chat Client‐4 leaves) 2. Broadcast messages to all connected clients Iterate over list of clients (but not the sender) and send message Multi‐Threading Synchronization: 2‐26 socket.setsockopt() in Python socket.error: [Error no 98] Address already in use Socket.setsockopt(level, optname, value:int) Set the value of the given socket option server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) SO_REUSEADDR flag tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire Multi‐Threading Synchronization: 2‐27 sys module and arguments in Python Running Python script from command line $ python test.py arg1 arg2 arg3 Python sys module provides access to any command‐line arguments via the sys.argv This serves two purposes: sys.argv is the list of command‐line arguments len(sys.argv) is the number of command‐line arguments sys.argv is the program i.e., script name This can be used to set the IP address and port number of the client/server from command line instead of hard‐coded them $ python chat-server.py 127.0.0.1 7005 Multi‐Threading Synchronization: 2‐28 Multi‐Threaded Chat Server Python TCPServer import modules import socket import sys create TCP socket from thread import * server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) set socket options server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # checks whether sufficient arguments have been provided check number of if len(sys.argv) != 3: passed args print ("missing arguments enter: ") exit() terminate the process Multi‐Threading Synchronization : 2‐29 Multi‐Threaded Chat Server Python TCPServer # first argument from command prompt is IP address IP_address = str(sys.argv) # second argument from command prompt is port number Port = int(sys.argv) # binds the server to an entered IP address and specified port number. bind to IP address server.bind((IP_address, Port)) and port number # listens for 10 active connections server.listen(10) list_of_clients = [] list to maintain all clients in the room. It contains connected clients’ sockets Multi‐Threading Synchronization : 2‐30 Multi‐Threaded Chat Server Python TCPServer def clientthread(conn, addr): thread to handle each client # sends a message to the client whose user object is conn send welcoming conn.send("Welcome to Network Programming chatroom!") message # broadcast to other that a new client has joined message_to_send = " joined" build the broadcast message call broadcast broadcast(message_to_send, conn) function while True: inside try block try: receive from client message = conn.recv(4096) message has content if message: # prints the message and address of the user who just sent the message print (": " + message) # call broadcast function to send message to all other clients build the broadcast message message_to_send = ": " + message Multi‐Threading broadcast(message_to_send, conn) call broadcast function Synchronization : 2‐31 Multi‐Threaded Chat Server Python TCPServer message has no content else: ''' message have no content if the connection is broken, then send message to others and remove the connection''' print("connection : disconnected") message_to_send = " left" build the broadcast call broadcast function broadcast(message_to_send, conn) message remove(conn) call remove function: remove client from available terminate client thread break clients list catch exception except: print("error occurred and ignored with: connection") continue ignore the error and continue Multi‐Threading Synchronization : 2‐32 Multi‐Threaded Chat Server Python TCPServer """ broadcast function is used to broadcast a message to all clients (but not the sender) """ def broadcast(message, connection): for client in list_of_clients: if client != connection: don’t send to yourself inside try block try: client.send(message) send message to client catch exception except: close client socket client.close() # if the link is broken, remove the client remove client from lists of remove(client) available clients Multi‐Threading Synchronization : 2‐33 Multi‐Threaded Chat Server Python TCPServer ''' remove function to remove the object from the list of clients ''' def remove(connection): if connection in list_of_clients: list_of_clients.remove(connection) remove client from lists of available clients Multi‐Threading Synchronization : 2‐34 Multi‐Threaded Chat Server Python TCPServer print("Welcome to Network Programming chatroom!\nServer is waiting for clients...") loop forever while True: """ accepts a connection request and stores two parameters: conn socket object and addr of the connected client""" conn, addr = server.accept() get connection socket and address of the connected client """ maintains a list to keep track of all available clients in the chatroom""" add client socket to list list_of_clients.append(conn) of available clients # prints the address of the user that just connected print (addr, addr, " joined") # creates an individual thread for every client create and start new start_new_thread(clientthread,(conn,addr)) thread for client conn.close() if server process exits, close Multi‐Threading Synchronization : 2‐35 server.close() both sockets Chat Room of Server and Clients: Clients send/receive messages Chat Client‐1 Chat Client‐3 Chat Server 1. Let other Clients know that Client‐1 sent “Hi all” 1. User might print messages while receiving Chat Client‐2 2. Send/Receive messages to Chat Client‐4 other clients via/From server Multi‐Threading Synchronization: 2‐36 select Module in Python select module provides access to I/O monitoring functions in most OS’s It allows high‐level and efficient I/O multiplexing Developers encouraged to use select module unless they want precise control over the OS‐level primitives select.select(): a direct interface call to the underlying OS‐level implementation It monitors sockets, open files, and pipes (connection between two processes) until they become readable or writable, or a communication error occurs Multi‐Threading Synchronization: 2‐37 select Module in Python: why to use select? Easier to monitor multiple connections at the same time More efficient than writing a polling loop in Python using socket timeouts The monitoring happens in the OS kernel network layer, instead of the interpreter select.select(rlist, wlist, xlist[, timeout]) The first three arguments are iterables of ‘waitable objects’ Either integers representing file descriptors or objects: rlist: wait until ready for reading wlist: wait until ready for writing xlist: wait for an “exceptional condition” The optional timeout argument specifies a time‐out as a floating point in seconds If timeout is omitted the function blocks until at least one file descriptor is ready Multi‐Threading Synchronization: 2‐38 select Module in Python: why to use select? select.select(rlist, wlist, xlist[, timeout]) Return a triple of lists of objects that are subsets of the first three arguments If the time‐out is reached without a file descriptor becoming ready, three empty lists are returned Multi‐Threading Synchronization: 2‐39 stdin and stdout sys module in Python Standard input: a file‐handler that a user program reads to get information from the user We give input to the standard input (stdin) Standard output: a user program writes normal information to this file‐handler The output is returned via the standard output (stdout) Python provides file‐like objects that represent stdin, stdout Python’s sys module provides file objects for stdin and stdout For the input file object, we use sys.stdin: keeps reading input from stdin For the output file object, we use sys.stdout: like sys.stdin, but it directly displays anything written to it to the console Multi‐Threading Synchronization: 2‐40 stdin example import sys import sys module stdin_file = sys.stdin standard input assigned print ("stdin_file object: ", stdin_file) print("Start writing into stdin") print("Ctrl+D indicates the End of File (EoF) for stdin") default EoF for stdin for line in stdin_file.readlines(): read and print each line written to stdin print("line: " + line.strip()) Multi‐Threading Synchronization: 2‐41 stdout example import sys import sys module stdout_file = sys.stdout standard output assigned print ("stdin_file object: ", stdout_file) inputs = ['Welcome', 'to', 'Network', 'Programming', 'Course!'] for word in inputs: stdout_file.write(word + '\n') print each word from the list to stdout Multi‐Threading Synchronization: 2‐42 Chat Client Python TCPClient import socket import select import select module import sys server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) create server check number of if len(sys.argv) != 3: socket passed args print ("missing arguments enter: ") exit() terminate the client IP_address = str(sys.argv) read ip address Port = int(sys.argv) read port number connect to server server.connect((IP_address, Port)) Multi‐Threading Synchronization : 2‐43 Chat Client Python TCPClient while True: # create a list to maintain possible input streams sockets_list = [sys.stdin, server] list of inputs to monitor """ Two possible inputs scenarios. Either the user enters text to send to other clients, or the server is sending a message to the client. """ """ select system call returns from sockets_list, the stream that is reader for input. So for example, if the server sent a message, then the if condition will hold true below. If the user wants to send a message, the else condition will evaluate as true""" print("wait on select call...") print before select call Multi‐Threading Synchronization : 2‐44 Chat Client list of inputs to monitor Python TCPClient select system call read_sockets, write_sockets, error_sockets = select.select(sockets_list,[],[]) print("select call returned") print after select call print("read_sockets: ", read_sockets) print the triple lists of objects that were #print("write_sockets: ", write_sockets) returned from select call #print("error_sockets: ", error_sockets) Multi‐Threading Synchronization : 2‐45 Chat Client Python TCPClient for socks in read_sockets: for each object in read_sockets server sent a message if socks == server: read server message message = socks.recv(4096) if message’s length is if(len(message) != 0): 0, server is down print(message) # server sent empty message, print error and leave else: print("Server is down, join later once it is up!") terminate the client exit() user wants to send else: a message message = sys.stdin.readline() read user message from standard input send user message to server.send(message) server sys.stdout.write("") write “” and user message to standard output sys.stdout.write(message) sys.stdout.flush() flush the buffer i.e., write everything in the buffer to the terminal Multi‐Threading Synchronization : 2‐46 server.close() if client process exits, close the sockets Chat Room of Server and Clients: Clients are idle (i.e., away) Chat Client‐1 ? Chat Client‐3 Chat Server 1. Let other Clients know that Client‐1 is away Chat Client‐2 Chat Client‐4 Application Layer: 2‐47 Chat Client – with user‐away feature Python TCPClient import socket import select import select module import sys import time import time module server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) create server socket check number of if len(sys.argv) != 3: passed args print ("missing arguments enter: ") exit() terminate the client IP_address = str(sys.argv) read ip address Port = int(sys.argv) read port number connect to server server.connect((IP_address, Port)) set the timeout time_out_period = 10 period (seconds) last_time_type = time.time() set last time user typed Multi‐Threading Synchronization : 2‐48 Chat Client – with user‐away feature Python TCPClient while True: # create a list to maintain possible input streams sockets_list = [sys.stdin, server] list of inputs to monitor """ Two possible inputs scenarios. Either the user enters text to send to other clients, or the server is sending a message to the client. """ """ select system call returns from sockets_list, the stream that is reader for input. So for example, if the server sent a message, then the if condition will hold true below. If the user wants to send a message, the else condition will evaluate as true""" print("wait on select call...") print before select call Multi‐Threading Synchronization : 2‐49 Chat Client – with user‐away feature list of inputs to monitor Python TCPClient select system call read_sockets, write_sockets, error_sockets = select.select(sockets_list,[],[], timeout period time_out_period) print("select call returned") print after select call print("read_sockets: ", read_sockets) print the triple lists of objects that were returned from select call #print("write_sockets: ", write_sockets) #print("error_sockets: ", error_sockets) Multi‐Threading Synchronization : 2‐50 Chat Client – with user‐away feature Python TCPClient if read_sockets: check if read_sockets is not empty (i.e., select timeout has not occurred) for socks in read_sockets: for each object in read_sockets server sent a message if socks == server: read server message message = socks.recv(4096) if message’s length is if(len(message) != 0): 0, server is down print(message) #check last time user typed text if (time.time() - last_time_type > time_out_period): check if user has not written # let the server know that user is away since time_out_period print("receive some text, but user is away") send away message to server server.send("is away") last_time_type = time.time() update last time user typed # server sent empty message, print error and leave else: print("Server is down, join later once it is up!") terminate the client exit() Multi‐Threading Synchronization : 2‐51 Chat Client – with user‐away feature Python TCPClient user wants to send else: a message read user message message = sys.stdin.readline() from standard input send user message to server.send(message) server sys.stdout.write("") write “” and user message to standard output sys.stdout.write(message) sys.stdout.flush() flush the buffer i.e., write everything in the buffer to the terminal last_time_type = time.time() update last time user typed else: read_sockets is empty (i.e., select timeout has occurred) print("select call returned empty after time-out") # let the server know that user is away send away message to server.send("is away") server server.close() if client process exits, close the sockets Multi‐Threading Synchronization : 2‐52

Use Quizgecko on...
Browser
Browser