LJ.← Portfolio

Selected coursework · CSUMB

CST 311: Introduction to Computer Networking

Course Objective:

Survey of Telecomm and Data comm Technology Fundamentals, Local Area Network, Wide Area Network, Internet and internetworking protocols including TCP/IP, network security and performance, emerging industry trends such as voice over the network and high speed networking. Designed as a foundation for students who wish to pursue more advanced network studies including certificate programs. Includes hands on networking labs that incorporate Cisco CCNA lab components.

TCP Client & Server:

During this course, we focused on the networking side of computers by learning how data is sent over a network. We learned the various ways that servers operate and how they communicate with one another through packets. A project that we worked on during our studies was a TCP Client & Server which was completed as a group project.

Client Code:

    # CST 311 - Programming Assignment 3 - TCP Client
    # This program acts as the client for the TCP client/server assignment
    # requirement, sending inputted messages to the server.
    # Team 4/BayTech - Luis Jimenez-Barrios, Warren Ngoun, Yukio Rivera, Jeannah Yasin
    from socket import *

    # Server Connection Variables
    serverName = '127.0.0.1'
    serverPort = 12000

    # Socket Variable and Connection
    clientSocket = socket(AF_INET, SOCK_STREAM)
    clientSocket.connect((serverName, serverPort))

    # While loop tracker
    # Only acts on two messages: A connection message & a final results message.
    while testCase:
        # Receive results, print them, prep tempSplit for later testing
        result = clientSocket.recv(1024).decode()
        print(result)
        tempSplit = result.split(" ")

        # Case for first Connection message, just check strings
        if (result == 'Client X Connected' or result == 'Client Y Connected'):
            message = input("Enter message to send to server: ")
            clientSocket.send(message.encode())

        # Case for FINAL connection message w/ first sender results
        # Checking by using Split to see the first few chars of 'X/Y:'
        elif(tempSplit[0] == 'X:' or tempSplit[0] == 'Y:'):
            testCase = False

    # Close down the socket once we've gotten final message
    clientSocket.close()

Server Code:

    # CST 311 - Programming Assignment 3 - TCP Server
    # Team 4/BayTech: Luis Jimenez-Barrios, Warren Ngoun, Yukio Rivera, Jennah Yasin
    # This program acts as the server for the TCP client/server assignment
    # requirement, the in-between message manager between Clients X & Y.

    # 11 - We need multithreading for this assignment to handle two different
    # clients concurrently. In other words, multithreading is needed so that the
    # server can handle multiple connections at the same time. Multithreading
    # prevents the recv connection socket line from blocking the main server thread
    # when more than one client attempts to access it as now each child thread will
    # block individually when they need to and our main/other threads can work as
    # needed.

    # Imports needed for sockets & threading/semaphores
    from multiprocessing import Semaphore
    from socket import *
    import threading

    serverPort = 12000      # port declaration

    # Create two binary lock semaphores for later mutexing
    firsConnCheck = Semaphore(1)
    secondCheck = Semaphore(1)

    # Lists for keeping track of data in-between threads, need lists b/c they
    # are mutable and so values can be changed.
    firstClientChecker = [0, 0]     # Variables for determining Client X/Y
    secondResponse = [0]        # Global list counter for keeping track of 2nd msg received
    clientMsg = [0, 0]      # List to keep track of text of first/second msg
    clientMsgCnt = [0]      # List to keep count of num of messages sent already

    threadTracker = [] # List to keep track of active threads

    class connectionThread (threading.Thread):
        def __init__(self, connectionSocket):
            threading.Thread.__init__(self)

            # Thread Object variables, needed for checking certain conditions
            self.connSock = connectionSocket
            self.firstResponse = [0]
            self.msgWhile = [0]
            self.threadID = ''
        
        def run(self):
            # Checks to see which connection will be labeled X or Y, prints
            # respective details too.
            # Uses semaphores to provide mutual exclusion to shared var.

            firstConnCheck.acquire()
            if(firstClientChecker[0] == 0):
                self.threadID = 'X'
                print("Acepted first connection, calling it client X")
                firstClientChecker[0] = 1
            else:
                self.threadID = 'Y'
                print("Accepted second connection, calling it client y\n")
                print("Waiting to receive messages from client X and Client Y...\n")
                secondResponse[0] = 1
            firstConnCheck.release()
        
            # First while loop tracks only the confirmation message that the server
            # sends upon both connections. If two connections haven't been
            # confirmed yet, it'll spin indefinitely waiting.

            while(self.firstResponse[0] < 1):
                # Test if a thread has sent its confirmation message to respective
                # client only AFTER Y has confirmed being connected.

                if (self.firstResponse[0] == 0 and secondResponse[0] == 1):
                    self.sendConfirmation()
                    self.firstResponse[0] = 1
        
            # After loop, threads WAIT for a response from their respective client
            result = self.connSock.recv(1024).decode()

            # On a msg, grab lock to ensure accurate msg receipt count & accurately
            # setting up the message array in charge of final confirmations.
            secondCheck.acquire()
            clientMsgCnt[0] += 1

            # Server-side confirmation print of received msg & its number
            print("Client " + self.threadID + " sent message " + str(clientMsgCnt[0]) + ": " + result)

            # if/elif to set up the actual list depending on which msg it is
            if (clientMsgCnt[0] == 1):
            clientMsg[0] = self.threadID + ": " + result
            elif (clientMsgCnt[0] == 2):
            clientMsg[1] = self.threadID + ": " + result
            secondCheck.release()

            # This while acts as a barrier to ensure that ONLY when both msgs have
            # been received will the threads send their final confirmations.
            while (self.msgWhile[0] == 0):
                # Semaphore lock ensures we're reading an accurate msg count
                secondCheck.acquire()
                if (clientMsgCnt[0] == 2):
                    self.msgWhile[0] = 1        # exit loop on confirmation of 2 recv msgs
                secondCheck.release()
        
            self.sendFinalConfirmation()        # Both threads can send their final msgs

        # Function lets each connectionThread send their respective connection
        # message back to their matching clients.
        def sendConfirmation(self):
            toSend = "Client " + self.threadID + " Connected"
            self.connSock.send(toSend.encode())

        # Function lets each connectionThread send the same message back to their
        # respective clients. Utilizes shared array to get the same results.
        def sendFinalConfirmation(self):
            toSend = str(clientMsg[0]) + " received before " + str(clientMsg[1])
            self.connSock.send(toSend.encode())
        
    # Create a TCP socket
    serverSocket = socket(AF_INET, SOCK_STREAM)

    # Assign IP address and port number to socket
    serverSocket.bind(('', serverPort))

    # Set socket to listen now & print waiting message.
    serverSocket.listen(1)
    print('The server is waiting to receive 2 connections...\n')

    # Only allows 2 active clients, other clients WONT connect and hang b/c there
    # will be no more opne accepting sockets.
    while(threading.active_count() < 3):

        # Per connection, create a thread and run it, set them aside to later join()
        connectionSocket, addr = serverSocket.accept()
        tempThread = connectionThread(connectionSocket)
        tempThread.start()
        threadTracker.append(tempThread)
    
    # Finally wait to join our threads back
    for thread in threadTracker:
        thread.join()
    
    # Final "waiting" and completion prints on socket close.
    print()
    print("Waiting a bit for clients to close their connection")
    connectionSocket.close()
    print("Done.")