Client server chat system with packet loss 

Programming Assignment

Project 1: Network Performance Measurement System 

In this project, you will develop a system for measuring the performance of the network (path) between two hosts A and B (from A to B). We will consider the following three metrics to measure the performance:

  • Packet loss rate: this is defined as the ratio of packets, which when sent from A to B, are lost by the network path in-between (much like what ping does).
  • UDP throughput: what is the maximum rate at which B can receive UDP segments sent from A.
  • TCP throughput: what is the maximum rate at which data can be transferred over a TCP connection betweenA and B, with data flowing from A to B.

(The UDP and TCP throughput measurements should also factor in any simulated packet losses) Write the corresponding programs to be run at machines A and B that perform the appropriate experiments to measure the above three metrics. Ideally, you should just have a single program which can either act as the source of the traffic or as the destination.

As a next step, add the following functionality. From any third machine, called the controller C, you should

be able to perform any of the three desired measurements from A to B. We should be able to do this at any time C chooses. So, controller C should issue an appropriate command which is communicated to A and B (in what order?). After this command issue, the appropriate measurement should be performed (using the same logic as earlier). And the measurement result should be conveyed back to C and the results of the measurements displayed. The controller program can be a separate program from your program todo the actual measurements.

Project 2: Client-Server Chat System

In this project, you have to write a chat program under the client-server architecture for real-time communication between 3 users. Specifically, the program should permit chat sessions between 2 or 3 users. In case of 3 users,messages generated by one user should reach the other two (e.g., like a conference). The chat program needs tallow a user to type messages, which should be conveyed to the other end(s) and displayed at the other end(s).

Note that under the client-server architecture, clients cannot directly communicate with each other and must communicate through a server. The user interface need not be sophisticated, but should be usable. It can be text-based or graphical.

Your program should only use UDP sockets and no TCP sockets. This means that you have to implementreliability (of the messages) on top of UDP, in your own program, without using TCP. (Note that you are simulatingpacket losses). You don’t need to worry about congestion/flow control, but you would need to ensure packetsre in order. You can choose to implement any type of reliability mechanism.

As a next step, implement a feature for the users to be able to exchange files. That is, in a two-user session chat between machines A and B, the user at machine A should be able to upload a file to machine B. The machine B’suser will choose whether or not to receive the file, and where the incoming file should be placed, and using what name. Check that your program works for both text files as well as binary files, and verify that the file has been received successfully and reliably. 

Solution

P1

import socket

import struct

import sys

import conf

import random as rd

import time

class Server:

def __init__(self, port):

self.port = port

def TestPacketLosses(self, count=1000):

self.sock = socket.socket()

self.sock.bind((”,self.port))

self.sock.listen(1)

self.conn = self.sock.accept()[0] #waiting for the connect.

received=0 #variable to count packets.

while True:

data = self.conn.recv(8) #received packets. L (long) packets are 8-bit length.

if data:

received=received+1

else:

break

self.conn.close()

return “Packet Losses:” + str(1 – float(received)/count)

def GetUdpSpeed(self, count=1000):

received=0

self.sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #UDP socket.

self.sock.bind((”,self.port))

t1 = time.clock()

while received < count:

data = self.sock.recv(8)

if data:

received=received+1

else:

break

t2 = time.clock()

dt = t2 – t1

self.sock.close()

return “UDP throughput:” + str(float(count)/(t2-t1))

def GetTcpSpeed(self, count=1000):

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #TCP socket.

self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) #disable Nagle algorithm

self.sock.bind((”,self.port))

self.sock.listen(1)

self.conn = self.sock.accept()[0]

received=0

t1 = time.clock()

while received < count:

data = self.conn.recv(8)

if data:

received=received+1

else:

break

t2 = time.clock()

return (“TCP throughput:” + str(float(count)/(t2-t1)))

self.conn.close()

class Client:

def __init__(self, host, port):

self.host = host

self.port = port

def TestPacketLosses(self, count=1000): #we send 1000 packets, and count on server, how much packets came.

self.sock = socket.socket()

self.sock.connect((self.host, self.port))

for i in xrange(count):

packet = struct.pack(‘L’,rd.randint(0,77777777))

if rd.random()>=conf.loss_prob: #simulated packet loss

self.sock.send(packet)

self.sock.close()

def GetUdpSpeed(self, count=2000): #we send 2000 packets, but we are waiting for only 100000. Because it’s udp packets, the haven’t status. We can’t just close connection.

self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

for i in xrange(count):

packet = struct.pack(‘L’,rd.randint(0,77777777))

if rd.random()>=conf.loss_prob:

self.sock.sendto(packet,(self.host, self.port))

self.sock.close()

def GetTcpSpeed(self): #we send as much packets, as we can. Server closes the connection if 1000 packets are received.

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

self.sock.connect((self.host, self.port))

while True:

try:

packet = struct.pack(‘L’,rd.randint(0,77777777))

if rd.random()>=conf.loss_prob:

self.sock.send(packet)

except:

self.sock.close()

break

class Controller:

def __init__(self,

server_host=’localhost’, server_port=50002,

client_host=’localhost’, client_port=50003, conn_port=50004):

self.client_host = client_host

self.client_port = client_port

self.server_host = server_host

self.server_port = server_port

self.conn_port = conn_port

def GetAllMeasures(self):

“””

  1. Tell to undef.
  2. Let them work.
  3. Get the data.

“””

self.servsock = socket.socket()

self.servsock.connect((self.server_host, self.server_port))

self.servsock.send(‘server ‘ + str(self.conn_port))

self.clientsock = socket.socket()

self.clientsock.connect((self.client_host, self.client_port))

self.clientsock.send(‘client ‘ + str(self.server_host) + ‘ ‘ + str(self.conn_port))

return self.servsock.recv(1024)

def UndefMain(port): #If program is undefined. Controller defines, is it server or listener.

sock = socket.socket()

sock.bind((”,int(port)))

sock.listen(1)

conn = sock.accept()[0]

data = conn.recv(1024)

data = data.split(‘ ‘)

if (data[0]==’server’):

conn.send(ServerMain(data[1]))

elif (data[0]==’client’):

ClientMain(data[1], data[2])

#a couple of main functions, to encapsulate the executive logic.

def ServerMain(port):

Me = Server(int(port))

ret = []

ret.append(Me.TestPacketLosses())

ret.append(Me.GetUdpSpeed())

ret.append(Me.GetTcpSpeed())

return “\n”.join(ret)

def ClientMain(host, port):

Me = Client(host, int(port))

Me.TestPacketLosses()

Me.GetUdpSpeed()

Me.GetTcpSpeed()

def ControllerMain(servhost, servport, clienthost, clientport, connport):

Me = Controller(servhost, int(servport), clienthost, int(clientport), int(connport))

return Me.GetAllMeasures()

def PrintUsage():

print “Usage: python ” + sys.argv[0] + ” client %host% %port%”

print “Or: python ” + sys.argv[0] + ” server %port%”

print “Or: python ” + sys.argv[0] + ” controller %servhost% %servport% %clienthost% %clientport% %connport%”

print “Or: python ” + sys.argv[0] + ” undef %port%”

print “Consider using screen or tmux if working on local machine.”

if __name__ == “__main__”:

if len(sys.argv)<2:

PrintUsage()

exit(0)

try:

if sys.argv[1] == ‘client’:

ClientMain(sys.argv[2],sys.argv[3])

elif sys.argv[1] == ‘server’:

print ServerMain(sys.argv[2])

elif sys.argv[1] == ‘controller’:

print ControllerMain(sys.argv[2],sys.argv[3],sys.argv[4],sys.argv[5],sys.argv[6])

elif sys.argv[1] == ‘undef’:

UndefMain(sys.argv[2])

else:

PrintUsage()

except Exception as exc:

PrintUsage()

print exc

P2

Client.py

import socket

import sys

import threading

import confclient as conf #our crafted config

import os

import random as rd

import hashlib #to verify files

from functools import partial # to better reading from file

def getdata(myport, logfile):

global savefile

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

sock.bind((”,myport))

while True: #receiving data from server

data = sock.recv(1024)

if data:

if data[:4]==’echo’:

with open(logfile, ‘a’) as lf:

lf.write(data[5:] + “\n”)

if data[:4]==’file’:

if savefile!=”N”:

proof = data.split(” “)[1] #sha512 hash

filesize = int(data.split(” “)[2])

while True:

filedata = sock.recv(1024)

if filedata!=”ENDFLAG”:

with open(savefile, ‘a’) as sf:

sf.write(filedata)

else:

break

if (proof==hashlib.sha512(open(savefile, ‘rb’).read()).hexdigest()):

print “File is received. ‘Savefile’ value changed to ‘N'”

savefile=’N’

else:

os.remove(savefile)

print “File isn’t received. Try again.”

def controller(logfile):

while True:

action = raw_input(“What do you want to do? s – to send message,” +

“f – to send file, ” +

“r – to receive file,” +

“t – to show last 10 messages, ” +

“h – to show all history, ” +

“q – to exit\n”)

if action == “q”:

os._exit(0) #kill all threads

elif action == “h”:

if os.path.isfile(logfile):

with open(logfile, ‘r’) as lf:

print “\n”.join(lf.readlines())

elif action == “t”:

if os.path.isfile(logfile):

with open(logfile, ‘r’) as lf:

msgs = lf.readlines()[10:] #last 10 messages

print “\n”.join(msgs)

elif action == “s”:

data = raw_input(“What message should I send?”)

S = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

if rd.random()>=conf.loss_prob:

S.sendto(‘echo ‘ + data,(conf.server[0],conf.server[1]))

S.close()

print “Success.”

elif action == “f”:

path = raw_input(“Please enter path to file:”)

if os.path.isfile(path):

S = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

filesize = os.path.getsize(path)

proof = hashlib.sha512(open(path, ‘rb’).read()).hexdigest()

if rd.random()>=conf.loss_prob:

S.sendto(‘file ‘ + proof + ” ” + str(filesize),(conf.server[0],conf.server[1]))

with open(path, ‘r’) as sf:

for chunk in iter(partial(sf.read, 1024), ”):

if rd.random()>=conf.loss_prob:

S.sendto(chunk,(conf.server[0],conf.server[1]))

if rd.random()>=conf.loss_prob:

S.sendto(“ENDFLAG”,(conf.server[0],conf.server[1]))

S.close()

print “Success.”

else:

print “There is no such file at this path.”

elif action == “r”:

global savefile

savefile = raw_input(“Please enter path to save received file:”)

print “Ok. This file can be received now. You’ve agreed on this. If not, set the path to ‘N'”

def PrintUsage():

print “Usage: python ” + sys.argv[0] + ” %clientport% %logfilepath%”

if __name__ == “__main__”:

global savefile

savefile = ‘N’ #to save the path of file to save.

if len(sys.argv)!=3:

PrintUsage()

exit(0)

try:

thr1 = threading.Thread(target=getdata,args=(int(sys.argv[1]),sys.argv[2],))

thr2 = threading.Thread(target=controller,args=(sys.argv[2],))

thr1.start()

thr2.start()

thr1.join()

thr2.join()

except Exception as exc:

PrintUsage()

print exc

confclient.py

server = (‘localhost’,7000)

loss_prob = 0.00 

confserver.py

clients = [(‘localhost’,7001),

(‘localhost’,7002),

(‘localhost’,7003)]

loss_prob = 0.00 

server.py

import socket

import sys

import confserver as conf #our crafted config

import random as rd

class Server:

def __init__(self, port, clients):

self.port = port

self.clients = clients

def startecho(self):

self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

self.sock.bind((”,self.port))

while True: #if we use this architecture, they always would be in order.

try:

data = self.sock.recv(1024)

if data:

if data[:4]==’echo’:

for client in self.clients:

if rd.random()>=conf.loss_prob:

self.sock.sendto(data,client)

if data[:4]==’file’:  #data format:  “file sha512 size”

for client in self.clients:

if rd.random()>=conf.loss_prob:

self.sock.sendto(data,client)

filesize = int(data.split(” “)[2])

while True:

filedata = self.sock.recv(1024)

if filedata:

if rd.random()>=conf.loss_prob:

self.sock.sendto(filedata,client)

if filedata==”ENDFLAG”:

break

except Exception as exc:

print exc

self.sock.close()

break

def ServerMain(port, clients):

Me = Server(int(port), clients)

Me.startecho()

def PrintUsage():

print “Usage: python ” + sys.argv[0] + ” %serverport%”

if __name__ == “__main__”:

if len(sys.argv)!=2:

PrintUsage()

exit(0)

try:

ServerMain(sys.argv[1],conf.clients)

except Exception as exc:

print exc