|
| 1 | +#!/usr/bin/env python |
| 2 | +""" |
| 3 | +Multi-threaded TCP Client |
| 4 | +multithreadedClient.py is a TCP client that maintains a maximum number of worker threads which continuously send a given |
| 5 | +number of requests to multithreadedServer.py and print the server's response. |
| 6 | +This is derived from an assignment for the Distributed Systems class at Bennington College |
| 7 | +""" |
| 8 | + |
| 9 | +from argparse import ArgumentParser |
| 10 | +from queue import Queue |
| 11 | +from socket import SO_REUSEADDR, SOCK_STREAM, error, socket, SOL_SOCKET, AF_INET |
| 12 | +from threading import Thread |
| 13 | + |
| 14 | +# -------------------------------------# |
| 15 | +########## ARGUMENT HANDLING ########## |
| 16 | +# -------------------------------------# |
| 17 | + |
| 18 | +# Initialize instance of an argument parser |
| 19 | +parser = ArgumentParser(description='Multi-threaded TCP Client') |
| 20 | + |
| 21 | +# Add optional arguments, with given default values if user gives no args |
| 22 | +parser.add_argument('-r', '--requests', default=10, type=int, help='Total number of requests to send to server') |
| 23 | +parser.add_argument('-w', '--workerThreads', default=5, type=int, help='Max number of worker threads to be created') |
| 24 | +parser.add_argument('-i', '--ip', default='127.0.0.1', help='IP address to connect over') |
| 25 | +parser.add_argument('-p', '--port', default=9000, type=int, help='Port over which to connect') |
| 26 | + |
| 27 | +# Get the arguments |
| 28 | +args = parser.parse_args() |
| 29 | + |
| 30 | + |
| 31 | +# --------------------------------------# |
| 32 | +########## CLIENT CONSTRUCTOR ########## |
| 33 | +# --------------------------------------# |
| 34 | + |
| 35 | + |
| 36 | +class Client: |
| 37 | + def __init__(self, id, address, port, message): |
| 38 | + self.s = socket(AF_INET, SOCK_STREAM) |
| 39 | + self.id = id |
| 40 | + self.address = address |
| 41 | + self.port = port |
| 42 | + self.message = message |
| 43 | + |
| 44 | + def run(self): |
| 45 | + try: |
| 46 | + # Timeout if the no connection can be made in 5 seconds |
| 47 | + self.s.settimeout(5) |
| 48 | + # Allow socket address reuse |
| 49 | + self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) |
| 50 | + # Connect to the ip over the given port |
| 51 | + self.s.connect((self.address, self.port)) |
| 52 | + # Send the defined request message |
| 53 | + self.s.send(self.message) |
| 54 | + # Wait to receive data back from server |
| 55 | + data = self.s.recv(1024) |
| 56 | + # Notify that data has been received |
| 57 | + print |
| 58 | + self.id, ": received: ", data |
| 59 | + # CLOSE THE SOCKET |
| 60 | + self.s.close() |
| 61 | + # If something went wrong, notify the user |
| 62 | + except error as e: |
| 63 | + print |
| 64 | + "\nERROR: Could not connect to ", self.address, " over port", self.port, "\n" |
| 65 | + raise e |
| 66 | + |
| 67 | + |
| 68 | +# ------------------------------------------------# |
| 69 | +########## DEFINE QUEUE WORKER FUNCTION ########## |
| 70 | +# ------------------------------------------------# |
| 71 | + |
| 72 | +# Create a queue to hold the tasks for the worker threads |
| 73 | +q = Queue(maxsize=0) |
| 74 | + |
| 75 | + |
| 76 | +# Function which generates a Client instance, getting the work item to be processed from the queue |
| 77 | +def worker(): |
| 78 | + message = "HELO" |
| 79 | + |
| 80 | + while True: |
| 81 | + # Get the task from teh work queue |
| 82 | + item = q.get() |
| 83 | + |
| 84 | + new_client = Client(item, args.ip, args.port, message) |
| 85 | + new_client.run() |
| 86 | + # Mark this task item done, thus removing it from the work queue |
| 87 | + q.task_done() |
| 88 | + |
| 89 | + |
| 90 | +# --------------------------------------------------# |
| 91 | +########## INITIATE CLIENT WORKER THREADS ########## |
| 92 | +# --------------------------------------------------# |
| 93 | + |
| 94 | +# Populate the work queue with a list of numbers as long as the total number of requests wished to be sent. |
| 95 | +# These queue items can be thought of as decrementing counters for the client thread workers. |
| 96 | +for item in range(args.requests): |
| 97 | + q.put(item) |
| 98 | + |
| 99 | +# Create a number of threads, given by the maxWorkerThread variable, to initiate clients and begin sending requests. |
| 100 | +for i in range(args.workerThreads): |
| 101 | + t = Thread(target=worker) |
| 102 | + t.daemon = True |
| 103 | + t.start() |
| 104 | + |
| 105 | +# Do not exit the main thread until the sub-threads complete their work queue |
| 106 | +q.join() |
0 commit comments