def startProtocol(self): """one_user_full_login_udp_server_test DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used.packet_stor """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport
def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport nomMovie = 0 for m in self.serverProxy.getMovieList(): nomMovie += 1 print("Movie title: ", m.movieTitle, m.movieId) print('ip port', self.serverProxy.getMovieAddrPort(m.movieTitle)[0]) print(nomMovie)
def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address (or the name) of the c2w server. .. attribute:: serverPort The port number used by the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ self.serverAddress = serverAddress self.serverPort = serverPort self.clientProxy = clientProxy self.lossPr = lossPr self.seqNum = 0 # sequence number for the next packet to be sent self.dHandler = DatagramHandler(self) self.userId = 0 self.userName = "" self.packReceived = False self.movieList = [] self.users = [] # userId: user self.state = state_code["disconnected"] self.movieRoomId = -1 # not in movie room self.currentMovieRoom = None def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendPacket(self, packet, callCount=0): """ param packet: Packet object param callCount: only used for the timeout mechanism. """ # the packet is received if packet.ack == 1: print "###sending ACK packet###:", packet buf = util.packMsg(packet) self.transport.write(buf.raw, (self.serverAddress, self.serverPort)) return if packet.seqNum != self.seqNum: return print "###sending packet###:", packet buf = util.packMsg(packet) self.transport.write(buf.raw, (self.serverAddress, self.serverPort)) callCount += 1 if callCount < attempt_num: reactor.callLater(timeout, self.sendPacket, packet, callCount) else: print "too many tries, packet:", packet," aborted" return def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The controller calls this function as soon as the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) self.roomType = 3 self.seqNum = 0 # reset seqNum self.userId = 0 # use reserved userId when login self.userName = userName loginRequest = Packet(frg=0, ack=0, msgType=0, roomType=self.roomType, seqNum=self.seqNum, userId=self.userId, destId=0, length=len(userName), data=userName) self.sendPacket(loginRequest) self.state = state_code["loginWaitForAck"] def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called **by the controller** when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ # This method is called when user clicks "send" button of any room destId = 0 if self.state == state_code["inMainRoom"]: roomType = room_type["mainRoom"] destId = 0 # for main room elif self.state == state_code["inMovieRoom"]: roomType = room_type["movieRoom"] destId = self.movieRoomId else: print "State error!" return messagePack = Packet(frg=0, ack=0, msgType=type_code["message"], roomType=roomType, seqNum=self.seqNum, userId=self.userId, destId=destId, length=len(message), data=message) self.sendPacket(messagePack) def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called **by the controller** when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ if roomName == ROOM_IDS.MAIN_ROOM: joinRoomRequest = Packet(frg=0, ack=0, msgType=type_code["roomRequest"], roomType=room_type["mainRoom"], seqNum=self.seqNum, userId=self.userId, destId=0, length=0, data=None) self.sendPacket(joinRoomRequest) self.state = state_code["waitForMainRoomAck"] self.movieRoomId = -1 else: roomId = [movie.roomId for movie in self.movieList if movie.movieName==roomName][0] joinRoomRequest = Packet(frg=0, ack=0, msgType=type_code["roomRequest"], roomType=room_type["movieRoom"], seqNum=self.seqNum, userId=self.userId, destId=roomId, length=0, data=None) self.sendPacket(joinRoomRequest) self.state = state_code["waitForMovieRoomAck"] self.currentMovieRoom = roomName self.movieRoomId = roomId def sendLeaveSystemRequestOIE(self): """ Called **by the controller** when the user has clicked on the leave button in the main room. """ LeaveSystemRequest=Packet(frg=0, ack=0, msgType=type_code["disconnectRequest"], roomType=room_type["notApplicable"], seqNum=self.seqNum, userId=self.userId, destId=0, length=0, data="") self.sendPacket(LeaveSystemRequest) def messageReceived(self, pack): # different action for different room type # find userName by id userName = [user.name for user in self.users if user.userId==pack.destId][0] if (pack.roomType == room_type["mainRoom"] or pack.roomType == room_type["movieRoom"]): self.clientProxy.chatMessageReceivedONE(userName, pack.data) def showMainRoom(self): """init the main room""" userList = util.adaptUserList(self.users) movieList = util.adaptMovieList(self.movieList) self.clientProxy.initCompleteONE(userList, movieList) def changeRoom(self): self.clientProxy.joinRoomOKONE() def updateUserList(self, movieName=None): """refresh the userList in the room""" userList = util.adaptUserList(self.users, movieName=movieName) self.clientProxy.setUserListONE(userList) def datagramReceived(self, datagram, (host, port)): """ :param string datagram: the payload of the UDP packet. :param host: the IP address of the source. :param port: the source port. Called **by Twisted** when the client has received a UDP packet. """ pack = self.dHandler.unpackMsg(datagram) if pack == None: return print "####packet received:", pack # the previous packet is received if pack.ack == 1 and pack.seqNum == self.seqNum: self.seqNum += 1 if pack.msgType == type_code["errorMessage"]: # error handling print "error message received:", error_decode[pack.data] print "state:", state_decode[self.state] if self.state == state_code["loginWaitForAck"]: # loginFailed self.clientProxy.connectionRejectedONE( error_decode[pack.data]) # back to login window else: print "unexpected error code" elif pack.msgType == type_code["loginRequest"]: # wait for movieList self.state = state_code["loginWaitForMovieList"] self.userId = pack.userId # get userId from server elif pack.msgType == type_code["roomRequest"]: if (pack.roomType == room_type["movieRoom"] and self.state == state_code["waitForMovieRoomAck"]): # This packet contains the ip and the port of the movie requested self.state = state_code["waitForMovieRoomUserList"] self.clientProxy.updateMovieAddressPort(self.currentMovieRoom, pack.data["ip"], pack.data["port"]) elif (pack.roomType == room_type["mainRoom"] and self.state == state_code["waitForMainRoomAck"]): self.state = state_code["waitForMainRoomUserList"] else: print "unexpected roomrequest ACK" elif pack.msgType == type_code["disconnectRequest"]: self.clientProxy.leaveSystemOKONE() self.clientProxy.applicationQuit() elif pack.msgType == type_code["message"]: pass else: print "Unexpected type of ACK packet" return if pack.msgType == type_code["movieList"]: # save movieList self.movieList = pack.data self.state = state_code["loginWaitForUserList"] elif pack.msgType == type_code["userList"]: # save userList self.users = pack.data if self.state == state_code["loginWaitForUserList"]: self.state = state_code["inMainRoom"] self.showMainRoom() elif self.state == state_code["inMainRoom"]: self.updateUserList() elif self.state == state_code["inMovieRoom"]: self.updateUserList(movieName=self.currentMovieRoom) elif self.state == state_code["waitForMovieRoomUserList"]: self.state = state_code["inMovieRoom"] self.changeRoom() self.updateUserList(movieName=self.currentMovieRoom) elif self.state == state_code["waitForMainRoomUserList"]: self.state = state_code["inMainRoom"] self.changeRoom() self.updateUserList() else: print "unexpected userList" elif pack.msgType == type_code["messageForward"]: self.messageReceived(pack) else: # type not defined print "type not defined on client side"
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.seq_number = 0 #This variable contains the sequence number used to track packet loss self.userID = 0 #This vatiable contains the id of the client that was set by the server (initially set to 0) self.lastEventID = 0 #This variable contains the id of the last event the client is currently aware of. self.userRoomID = 0 #This variable contains the room the client is currently in. self.futureRoomID = 0 #This variable contains the room the client want to switch to self.entry_number_awaited = 0 #This variable contains the number of entry the client is expecting from the next multi-entry response self.packet_stored = 0 #This variable contains a packet to be resent if its answer is not received. When it is equals to 0 there is no packet to resend. self.packet_awaited = 16 #This variable contains the type of the next packet to be received. When it is equals to 16 there is no packet to be received. self.store = c2wClientModel( ) #The c2wClientModel is a class used to store all data about users and movies self.delay = 0.5 #The length of the timer that is armed whenever the client send a request. When it runs out the client resend the message if no answer was given. self.pingTimer = 1 #The time to wait before sending a ping after having received last pong self.resendTries = 0 #The number of times the resend function has resent the message self.store.addMovie(ROOM_IDS.MAIN_ROOM, '0.0.0.0', '0', 0) def startProtocol(self): """one_user_full_login_udp_server_test DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used.packet_stor """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def incrementSeqNumber(self): if self.seq_number >= 65536: self.seq_number = 0 else: self.seq_number = self.seq_number + 1 ########### The function that resends packets whenever the timer runs out. #OK def resend_packet(self, seq_number): if self.packet_awaited != 16 and self.packet_stored != 0 and self.seq_number == seq_number: #Check if there is a packet to be resent if self.resendTries < 10: print('resending :') print(self.packet_stored) self.transport.write(self.packet_stored, (self.serverAddress, self.serverPort)) self.resendTries += 1 reactor.callLater(self.delay, self.resend_packet, seq_number) else: self.clientProxy.applicationQuit() ########### PUT_LOGIN #OK def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) packet = packing.PUT_LOGIN(self.seq_number, userName) print('login :'******'Message request called with content=%s', message) packet = packing.PUT_NEW_MESSAGE(self.seq_number, self.userID, self.userRoomID, message) print('new message :') print(packet) self.transport.write(packet, (self.serverAddress, self.serverPort)) self.packet_stored = packet self.packet_awaited = 15 reactor.callLater(self.delay, self.resend_packet, self.seq_number) ########### PUT_SWITCH_ROOM #OK def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ moduleLogger.debug('Join Room request called with room name = %s', roomName) self.futureRoomID = self.store.getMovieByTitle(roomName).movieId packet = packing.PUT_SWITCH_ROOM(self.seq_number, self.userID, self.futureRoomID) print('put switch room :') print(packet) self.transport.write(packet, (self.serverAddress, self.serverPort)) self.packet_stored = packet self.packet_awaited = 13 reactor.callLater(self.delay, self.resend_packet, self.seq_number) ########### PUT_LOGOUT #OK def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ moduleLogger.debug('Leave system request called') packet = packing.PUT_LOGOUT(self.seq_number, self.userID) print('logout :') print(packet) self.transport.write(packet, (self.serverAddress, self.serverPort)) self.packet_stored = packet self.packet_awaited = 3 reactor.callLater(self.delay, self.resend_packet, self.seq_number) ########### GET_PING #OK def sendGetPingRequestOIE(self): """ This function is used by the chatClientProtocol to get ping from the server """ if self.packet_awaited == 16: #Check if there is an awaited response first, if not start the ping moduleLogger.debug('Get ping request called') packet = packing.GET_PING(self.seq_number, self.userID, self.lastEventID, self.userRoomID) print('get_ping :') print(packet) self.transport.write(packet, (self.serverAddress, self.serverPort)) self.packet_stored = packet self.packet_awaited = 5 reactor.callLater(self.delay, self.resend_packet, self.seq_number) else: #If there is, report the ping at a later date (to make sure there is never two requests sent by the client and not answered at once) reactor.callLater(self.pingTimer, self.sendGetPingRequestOIE) ########### GET_EVENTS #OK def sendGetEventsRequestOIE(self, nbr_events): """ This function is used by the chatClientProtocol when a ping from the server has revealed that the client is not synchronized yet """ moduleLogger.debug('Get events request called') packet = packing.GET_EVENTS(self.seq_number, self.userID, self.lastEventID, nbr_events, 0) print('get events :') print(packet) self.transport.write(packet, (self.serverAddress, self.serverPort)) self.packet_stored = packet self.packet_awaited = 7 reactor.callLater(self.delay, self.resend_packet, self.seq_number) ########### GET_ROOMS #OK def sendGetRoomsRequestOIE(self, first_room_id, nbr_rooms): """ This fuction is used by the chatClientProtocol to get a list of current rooms """ moduleLogger.debug('Get rooms request called') packet = packing.GET_ROOMS(self.seq_number, self.userID, first_room_id, nbr_rooms) print('get rooms :') print(packet) self.transport.write(packet, (self.serverAddress, self.serverPort)) self.packet_stored = packet self.packet_awaited = 9 reactor.callLater(self.delay, self.resend_packet, self.seq_number) ########### GET_USERS #OK def sendGetUsersRequestOIE(self, first_user_id, nbr_users): """ This function is used by the chatClientProtocol to get a list of current users """ moduleLogger.debug('Get users request called') packet = packing.GET_USERS(self.seq_number, self.userID, first_user_id, nbr_users, self.userRoomID) print('get_users :') print(packet) self.transport.write(packet, (self.serverAddress, self.serverPort)) self.packet_stored = packet self.packet_awaited = 11 reactor.callLater(self.delay, self.resend_packet, self.seq_number) ########### def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ fieldsList = unpacking.decode(datagram) if fieldsList[0][0] == self.packet_awaited and fieldsList[0][ 1] == self.seq_number: #Check if the message received is the one that is awaited (type and sequence number check) print('Correct packet received...') print(fieldsList) self.packet_stored = 0 self.packet_awaited = 16 self.resendTries = 0 self.incrementSeqNumber() ########### RESPONSE_LOGIN OK if fieldsList[0][0] == 1: print('Received the login response...') if fieldsList[1][0] == 0: print('Login status : Done') self.userID = fieldsList[1][1] self.lastEventID = fieldsList[1][2] self.sendGetUsersRequestOIE(1, 255) elif fieldsList[1][0] == 1: self.clientProxy.connectionRejectedONE('Unknow error') print('Login status : Unknown error') elif fieldsList[1][0] == 2: self.clientProxy.connectionRejectedONE('Too many users') print('Login status : Too many users') elif fieldsList[1][0] == 3: self.clientProxy.connectionRejectedONE('Invalid username') print('Login status : Invalid username') elif fieldsList[1][0] == 4: self.clientProxy.connectionRejectedONE( 'Username not available') print('Login status : Username not available') ########### RESPONSE_LOGOUT if fieldsList[0][0] == 3: print('Received the logout response...') self.userID = 0 self.lastEventID = 0 self.seq_number = 0 self.packet_stored = 0 self.packet_awaited = 16 self.store.removeAllMovies() self.store.removeAllUsers() self.clientProxy.leaveSystemOKONE() moduleLogger.debug('Logout status : Done') ########### RESPONSE_PING elif fieldsList[0][0] == 5: print('Received the ping response...') lastServerEventID = fieldsList[1][0] if self.lastEventID != lastServerEventID: print( 'Ping status : Not up-to-date, getting events required' ) self.sendGetEventsRequestOIE(lastServerEventID - self.lastEventID) else: print('Ping status : up-to-date, preparing next ping') reactor.callLater(self.pingTimer, self.sendGetPingRequestOIE) ########### RESPONSE_EVENTS elif fieldsList[0][0] == 7: print('Received the get_events response...') for i in range(len(fieldsList[1])): if fieldsList[1][i][1] == 1: #Message event print('Message event received !') self.lastEventID = fieldsList[1][i][0] user = self.store.getUserById(fieldsList[1][i][3]) print(user) if user.userChatRoom == self.userRoomID and user.userId != self.userID: self.clientProxy.chatMessageReceivedONE( user.userName, fieldsList[1][i][4]) print(fieldsList[1][i][4]) elif fieldsList[1][i][1] == 2: #New user event print('A new user has been added !') self.lastEventID = fieldsList[1][i][0] room = self.store.getMovieById(fieldsList[1][i][2]) self.store.addUser(fieldsList[1][i][4], fieldsList[1][i][3], room.movieId) self.clientProxy.userUpdateReceivedONE( fieldsList[1][i][4], room.movieTitle) moduleLogger.debug('GetEvents status : New user') elif fieldsList[1][i][1] == 3: #Switch room event print('A user switched room') self.lastEventID = fieldsList[1][i][0] name = self.store.getUserById( fieldsList[1][i][3]).userName room = self.store.getMovieById( fieldsList[1][i][4]).movieTitle self.store.updateUserChatroom(name, fieldsList[1][i][4]) self.clientProxy.userUpdateReceivedONE(name, room) moduleLogger.debug( 'GetEvent status : User switched room') elif fieldsList[1][i][1] == 4: #Logout event print('A user disconnected !') self.lastEventID = fieldsList[1][i][0] name = self.store.getUserById( fieldsList[1][i][3]).userName self.store.removeUser(name) self.clientProxy.userUpdateReceivedONE( name, ROOM_IDS.OUT_OF_THE_SYSTEM_ROOM) moduleLogger.debug('GetEvent status : User logged out') print('GetEvent status : up-to-date, preparing next ping') reactor.callLater(self.pingTimer, self.sendGetPingRequestOIE) ########### RESPONSE_ROOMS elif fieldsList[0][0] == 9: print('Received the get_room response...') #FieldsList returns the data in the following form : Room_id, IP, Port, Room_name, Nbr_users #AddMovie accepts it in the following form : Title, IP, Port, ID moduleLogger.debug('Room status : Rooms list received') for i in range(len(fieldsList[1])): if self.store.getMovieById(fieldsList[1][i][0]) is None: self.store.addMovie(fieldsList[1][i][3], fieldsList[1][i][1], fieldsList[1][i][2], fieldsList[1][i][0]) c2wMovies = self.store.getMovieList( ) #get the movie list in the appropriate format movieList = [] for i in range(len(c2wMovies)): if c2wMovies[i].movieTitle != ROOM_IDS.MAIN_ROOM: movieList.append((c2wMovies[i].movieTitle, c2wMovies[i].movieIpAddress, c2wMovies[i].moviePort)) c2wUsers = self.store.getUserList( ) #get the user list in the appropriate format print(c2wUsers) userList = [] for i in range(len(c2wUsers)): userList.append((c2wUsers[i].userName, self.store.getMovieById( c2wUsers[i].userChatRoom).movieTitle)) print(userList) self.clientProxy.initCompleteONE( userList, movieList) #send both to the UI print('Room status : UI has been updated') print('Room status : UI is ready, starting ping cycle') reactor.callLater( self.pingTimer, self.sendGetPingRequestOIE ) #Then starts the Ping - Pong - GetEvents - ResponseEvents - Ping cycle ########### RESPONSE_USERS elif fieldsList[0][0] == 11: print('Received the get_user response...') #FieldsList returns the data in the following form : user_id, user_name, room_id #AddUser accepts it in the following form : Name, ID, Chatroom moduleLogger.debug('Users status : Users list received') print(fieldsList) for i in range(len(fieldsList[1])): print("AAA") if not self.store.userExists(fieldsList[1][i][1]): self.store.addUser(fieldsList[1][i][1], fieldsList[1][i][0], fieldsList[1][i][2]) print( 'Users status : Users list fully updated, asking for rooms' ) self.sendGetRoomsRequestOIE(1, 255) ########### RESPONSE_SWITCH_ROOM elif fieldsList[0][0] == 13: print('Received the switch room response...') if fieldsList[1][0] == 0: self.clientProxy.joinRoomOKONE() self.userRoomID = self.futureRoomID print('Switch room status : Done') elif fieldsList[1][0] == 1: print('Switch room status : Unknow error') ########### RESPONSE_NEW_MESSAGE elif fieldsList[0][0] == 15: print('Received the put_new_message response') if fieldsList[1][0] == 0: print('Message status : transmitted') elif fieldsList[1][0] == 1: print('Message status : Unknown error') self.clientProxy.chatMessageReceivedONE( 'Server', 'Message status : Unknown error') elif fieldsList[1][0] == 2: print('Message status : Invalid room') self.clientProxy.chatMessageReceivedONE( 'Server', 'Message status : Invalid room') elif fieldsList[1][0] == 3: print( 'Message status : Incorrect room (You must send a message only into your current room)' ) self.clientProxy.chatMessageReceivedONE( 'Server', 'Message status : Incorrect room (You must send a message only into your current room)' )
class c2wUdpChatServerProtocol(DatagramProtocol): lastMessage = {} #Id du dernier message envoyé à un Host Port tempUsernames = {} seqUsers = {} #list des seq de chaque utilsateur attenteUsers = { } #dico permettant de stocker une variable indiquant le nombre de fois que l'on a envoyé un message sans réponse ACKUsers = { } #Chaque valeur vaut 1 ou 0 pour un host_port donné. Si 1 : on vient de recevoir un ACK (on arrete le reacteur de l'utilisateur), tant valeur = 0 on continue le bouclage waitForACK = {} #Dico stockant les reacteurs seq = 1 #Seq du serveur def __init__(self, serverProxy, lossPr): """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ msg = struct.unpack_from('!hh' + str(len(datagram) - 4) + 's', datagram) taille = msg[0] st = msg[1] #Seq + Type data = datagram if taille == len( datagram): #vérification que la taille du message est conforme seqType = self.getSeqType( st) #Tupple contenant la Sequence et le Type (nseq, tTrame) = seqType if tTrame != 63: print('ack sent respons to ' + str(seqType[1])) self.sendAck(seqType[0], host_port) if tTrame == 1: #Le message reçu est une inscription user = (data.decode('utf-8'))[4:] print("inscription received from " + user) check = self.checkInscription(user) if check != 0: print("inscription refused for " + user) msg = bytearray(1) struct.pack_into('b', msg, 0, check) self.lastMessage[host_port] = 8 self.sender( 1, 8, msg, host_port ) #Envoie de l'erreur d'inscription avec le code d'erreur associé (check = 1, 2 ou 3) else: print("inscription accepted for " + user) self.lastMessage[ host_port] = 7 #On initie le dernier message de l'utilisateur à 7 (Inscription OK) self.tempUsernames[host_port] = user self.ACKUsers[host_port] = 0 self.attenteUsers[host_port] = 0 self.inscriptionAccepted( host_port ) #Envoie du msg 7 : inscription acceptée. Doit se faire APRES l'ajout de l'user if tTrame == 5: #MESSAGE RECU print('msg received from ' + (self.serverProxy.getUserByAddress(host_port).userName) + ' and relayed') self.lastMessage[ host_port] = 5 #Le dernier message reçu de l'utilisateur est "l'envoie d'un msg instantanné self.redirchat(msg[2], host_port) #On redirige le message if tTrame == 6: #JOINDRE UN SALON print('user ' + (self.serverProxy.getUserByAddress(host_port).userName) + ' trying to join room') self.lastMessage[host_port] = 6 self.join_room_response(datagram, host_port) if tTrame == 9: #LEAVING print('user ' + (self.serverProxy.getUserByAddress(host_port).userName) + ' leaving') self.lastMessage[host_port] = 5 self.userUpdate( self.serverProxy.getUserByAddress(host_port).userName, 127, host_port) self.serverProxy.removeUser( self.serverProxy.getUserByAddress(host_port).userName) print(self.serverProxy.getUserList()) if tTrame == 63: #ACK RECU print('ack received') if self.lastMessage[host_port] == 7: if (nseq + 1 == self.seq): if ( self.ACKUsers[host_port] == 0 ): #Si on attendait un ACK de l'utilisateur, on cancel le bouclage du racteur self.waitForACK[host_port].cancel( ) #On cancel le bouclage du Send&Wait self.ACKUsers[host_port] = 1 self.seqUsers[ host_port] = 2 #On initie la sequence de l'utilisateur à 1 self.serverProxy.addUser( self.tempUsernames[host_port], 0, None, host_port ) #Ajout de l'utilisateur dans la base de donnée #self.userUpdate((self.serverProxy.getUserByAddress(host_port).userName), 0, host_port) #Envoie la mise a jour aux autres utilisateurs self.lastMessage[ host_port] = 2 #On indique que le dernier message envoyé est la liste des firlm print('sending list of films to ' + (self.serverProxy.getUserByAddress( host_port).userName)) self.ACKUsers[ host_port] = 0 #Comme on envoie un message, on initie la valeur de l'ACK de l'utilisateur à 0 (on n'a pas reçu d'acquittements) self.attenteUsers[ host_port] = 0 #Pareil que l'ACK mais pour le nombre de tentative. self.sendListFilm(host_port) #On envoie les films. elif self.lastMessage[host_port] != 8: if (nseq + 1 == self.seqUsers[host_port]): if ( self.ACKUsers[host_port] == 0 ): #Si on attendait un ACK de l'utilisateur, on cancel le bouclage du racteur self.waitForACK[host_port].cancel() self.ACKUsers[host_port] = 1 if self.lastMessage[ host_port] == 2: #Si le dernier message est l'envoie de film self.lastMessage[ host_port] = 3 #On envoie la liste des utilisateurs print('sending list of users to ' + (self.serverProxy.getUserByAddress( host_port).userName)) self.ACKUsers[host_port] = 0 self.attenteUsers[host_port] = 0 self.sendListUsers(host_port) self.userUpdate((self.serverProxy.getUserByAddress( host_port).userName), 0, host_port) print("uizef") #ENVOIE DE L'INSCRIPTION ACCEPTEE def inscriptionAccepted(self, host_port): if ((self.attenteUsers[host_port] < 10) and (self.ACKUsers[host_port] == 0)): if self.attenteUsers[host_port] != 0: self.seq += -1 self.sender(0, 7, None, host_port) #Envoie du message Inscription OK self.waitForACK[host_port] = reactor.callLater( 1, self.inscriptionAccepted, host_port) self.attenteUsers[host_port] += 1 elif self.attenteUsers[host_port] == 10: print("The new user did not send any answer") #ENVOIE LA LISTE DE FILM def sendListFilm(self, host_port): if ((self.attenteUsers[host_port] < 10) and (self.ACKUsers[host_port] == 0)): taille = 0 LF = self.serverProxy.getMovieList() for i in LF: taille += len(i.movieTitle) + 8 buffer = bytearray(taille) n = 0 for i in LF: title, port, ip, ide = i.movieTitle, i.moviePort, i.movieIpAddress, i.movieId title = title.encode('utf-8') struct.pack_into( '!b', buffer, n, len(title) + 8) #taille dans le buffer 8 = taille,ip ,port,id ipe = ip.split('.') for i in range(4): struct.pack_into('b', buffer, n + 1 + i, int(ipe[i])) form = '!hb' + str(len(title)) + 's' struct.pack_into(form, buffer, n + 5, port, ide, title) n += 8 + len(title) self.sender(taille, 2, buffer, host_port) self.waitForACK[host_port] = reactor.callLater( 1, self.sendListFilm, host_port) self.attenteUsers[host_port] += 1 elif self.attenteUsers[host_port] == 10: print('user ' + (self.serverProxy.getUserByAddress(host_port).userName) + ' has been removed from database') self.serverProxy.removeUser( self.serverProxy.getUserByAddress(host_port).userName) #ENVOIE LA LISTE DES USERS def sendListUsers(self, host_port): if ((self.attenteUsers[host_port] < 10) and (self.ACKUsers[host_port] == 0)): taille = 0 for i in self.serverProxy.getUserList(): taille += len(i.userName) + 2 buffer = bytearray(taille) n = 0 for i in self.serverProxy.getUserList(): user, idroom = i.userName, i.userChatRoom if idroom != 0: idroom = self.serverProxy.getMovieById( i.userChatRoom).movieId print(idroom) user = user.encode('utf8') form = '!bb' + str(len(user)) + 's' struct.pack_into( form, buffer, n, len(user) + 1 + 1, idroom, user ) #taille données=longueur titre + 1 octet de taille + 2 + 4 + 1) suivie par ip, port etc.. n += len(user) + 2 self.sender(taille, 3, buffer, host_port) self.waitForACK[host_port] = reactor.callLater( 1, self.sendListUsers, host_port) self.attenteUsers[host_port] += 1 elif self.attenteUsers[host_port] == 10: print('user ' + (self.serverProxy.getUserByAddress(host_port).userName) + ' has been removed from database') self.serverProxy.removeUser( self.serverProxy.getUserByAddress(host_port).userName) #METTRE A JOUR UN USER def userUpdate(self, userName, idSalon, host_port): userName = userName.encode('utf-8') buffer = bytearray(len(userName) + 1) struct.pack_into('b' + str(len(userName)) + 's', buffer, 0, idSalon, userName) for i in self.serverProxy.getUserList(): hp = i.userAddress #if host_port!=hp: print('sending update') self.ACKUsers[hp] = 0 self.attenteUsers[hp] = 0 self.sendUserUpdate((len(userName) + 1, buffer, hp)) def sendUserUpdate(self, touple): taille = touple[0] buffer = touple[1] host_port = touple[2] if ((self.attenteUsers[host_port] < 10) and (self.ACKUsers[host_port] == 0)): self.sender(taille, 4, buffer, host_port) self.waitForACK[host_port] = reactor.callLater( 1, self.sendUserUpdate, touple) self.attenteUsers[host_port] += 1 elif self.attenteUsers[host_port] == 10: print('user ' + (self.serverProxy.getUserByAddress(host_port).userName) + ' has been removed from database') self.serverProxy.removeUser( self.serverProxy.getUserByAddress(host_port).userName) def sendRedirChat(self, taille, buffer, host_port): if ((self.attenteUsers[host_port] < 10) and (self.ACKUsers[host_port] == 0)): self.sender(taille, 10, buffer, host_port) self.waitForACK[host_port] = reactor.callLater( 1, self.sendRedirChat, (taille, buffer, host_port)) self.attenteUsers[host_port] += 1 elif self.attenteUsers[host_port] == 10: print('user ' + (self.serverProxy.getUserByAddress(host_port).userName) + ' has been removed from database') self.serverProxy.removeUser( self.serverProxy.getUserByAddress(host_port).userName) #REDIRIGE CHAT def redirchat(self, message, host_port): user = self.serverProxy.getUserByAddress(host_port).userName taille_user = len(user) user = user.encode('utf-8') taille_msg = len(message) taille_data = taille_user + taille_msg + 1 #+1 car taille de l'utilisateur est sur 1 octet msg = bytearray(taille_data) form = '!b' + str(taille_user) + 's' + str(taille_msg) + 's' struct.pack_into(form, msg, 0, taille_user, user, message) for i in self.serverProxy.getUserList(): if i.userChatRoom == self.serverProxy.getUserByAddress( host_port).userChatRoom: hp = i.userAddress #if host_port!=hp: self.ACKUsers[hp] = 0 self.attenteUsers[hp] = 0 self.sendRedirChat(taille_data, msg, hp) #REPONSE A UNE DEMANDE D'ACCES A UNE ROOM def join_room_response(self, datagram, host_port): form = '!hhb' data = struct.unpack_from(form, datagram) id_movie = data[2] user = self.serverProxy.getUserByAddress(host_port).userName if id_movie == 0: self.sender(0, 11, None, host_port) self.serverProxy.updateUserChatroom(user, ROOM_IDS.MAIN_ROOM) self.userUpdate(user, 0, host_port) else: m = self.serverProxy.getMovieById(id_movie) if m == None: self.sender(0, 12, None, host_port) else: self.sender(0, 11, None, host_port) self.serverProxy.updateUserChatroom(user, m.movieId) self.userUpdate(user, m.movieId, host_port) n = 0 for i in self.serverProxy.getUserList(): if i.userChatRoom != ROOM_IDS.MAIN_ROOM and self.serverProxy.getMovieById( i.userChatRoom ) != None and self.serverProxy.getMovieById( i.userChatRoom).movieId == m.movieId: n += 1 if n == 1: self.serverProxy.startStreamingMovie(m.movieTitle) #def leave_system_response(self,datagram,host_port): #ENVOIE D'UN ACK def sendAck(self, seq, host_port): typeM = self.dec2bin(63, 6) seqM = self.dec2bin(seq, 10) st = seqM + typeM msg = bytearray(4) struct.pack_into('!hh', msg, 0, 4, int(st, 2)) self.transport.write(msg, host_port) #METHODE ASSEMBLANT LE MESSAGE ET L'ENTETE def sender(self, taille, typeM, data, host_port): if ( (typeM != 8) and (typeM != 7) ): #7 ou 8 = Inscription acceptée/Refusée ===> User n'est pas encore dans la base de donnée du serveur user = self.serverProxy.getUserByAddress( host_port ).userName #on trouve l'utilisateur à qui on envoie le message seqM = self.dec2bin(self.seqUsers[host_port], 10) self.seqUsers[host_port] += 1 else: user = None seqM = self.dec2bin( self.seq, 10 ) #Si l'utilisateur n'existe pas, on envoie avec la séquence du serveur self.seq += 1 typeM = self.dec2bin(typeM, 6) #conversion du type sur 6 bit st = seqM + typeM #on concatene sequence et type buf = bytearray(4) struct.pack_into('!hh', buf, 0, taille + 4, int(st, 2)) if data != None: buf = buf + data self.transport.write(buf, host_port) #VERIFICATION DE L'INSCRIPTION def checkInscription(self, data): check = 0 if (data.find(" ") != -1): check = 3 if len(data) > 127: #Username > 254 caractere = 254 octets check = 2 if self.serverProxy.userExists(data): check = 1 return check #CONVERTISSEUR BINAIRE def dec2bin(self, decimal, nbbits): #Le convertisseur fait une division euclidienne if decimal == 0: return "0".zfill(nbbits) result = "" while decimal != 0: decimal, rest = divmod(decimal, 2) result = "01"[rest] + result return result.zfill(nbbits) #RETOURNE lE NUM DE SEQ ET LE TYPE DE TRAME def getSeqType(self, st): st = str(self.dec2bin(st, 16)) seq = st[:-6] types = st[-6:] return int(seq, 2), int(types, 2) #tupple[0] = sequence, tupple[1] = type
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #initializing the sequence number to zero self.seql = 0 #initializing the resend counter to zero self.count = 0 self.repeat = None self.movielist = [] self.userlist = [] self.ackcm = None self.ackleave = None self.username = None #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ #total length of the loginrequest packet length = len(userName) + 4 self.username = userName #combined sequence number and type number sequ_type = (self.seql << 4) | (types["Envoi du Pseudo"]) buf = struct.pack('!HH%is' % len(userName), length, sequ_type, userName.encode('utf-8')) self.transport.write(buf, (self.serverAddress, self.serverPort)) #retransmit the loginrequest if lost. if (self.count < 7): self.repeat = reactor.callLater(1, self.sendLoginRequestOIE, userName) self.count += 1 moduleLogger.debug('loginRequest called with username=%s', userName) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ lenuser = len(self.username.encode('utf-8')) length = 4 + 1 + lenuser + len(message.encode('utf-8')) buf = bytearray(length) offset = 4 sequ_type = (self.seql << 4) | (types["Message Chat"]) struct.pack_into('!HH', buf, 0, length, sequ_type) struct.pack_into('!B%is' % len(self.username.encode('utf-8')), buf, offset, lenuser, self.username.encode('utf-8')) offset += 1 + lenuser struct.pack_into('!%is' % len(message.encode('utf-8')), buf, offset, message.encode('utf-8')) self.transport.write(buf, (self.serverAddress, self.serverPort)) #retransmit the chatmessage if lost. if (self.count < 7): self.repeat = reactor.callLater(1, self.sendChatMessageOIE, message) self.count += 1 pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ if (roomName is ROOM_IDS.MAIN_ROOM): #total length of the Quitter salle film packet length = 4 #combined sequence number and type number sequ_type = (self.seql << 4) | (types["Quitter salle film"]) self.ackcm = self.seql #Packing and sending the Quitter salle film packet buf = struct.pack('!HH', length, sequ_type) self.transport.write(buf, (self.serverAddress, self.serverPort)) else: #total length of the Choix d’un film packet length = len(roomName) + 4 #combined sequence number and type number sequ_type = (self.seql << 4) | (types["Choix d’un film"]) self.ackcm = self.seql #Packing and sending the Choix d’un film packet buf = struct.pack('!HH%is' % len(roomName), length, sequ_type, roomName.encode('utf-8')) self.transport.write(buf, (self.serverAddress, self.serverPort)) #retransmit the Choix d’un film or Quitter salle film if lost. if (self.count < 7): self.repeat = reactor.callLater(1, self.sendJoinRoomRequestOIE, roomName) self.count += 1 pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ #total length of the leaveSystemRequest packet length = 4 #combined sequence number and type number sequ_type = (self.seql << 4) | (types["Quitter application"]) self.ackleave = self.seql #Packing and transmitting the leaveSystemRequest packet. buf = struct.pack('!HH', length, sequ_type) self.transport.write(buf, (self.serverAddress, self.serverPort)) #retransmit the leaveSystemRequest packet if lost. if (self.count < 7): self.repeat = reactor.callLater(1, self.sendLeaveSystemRequestOIE) self.count += 1 pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ #unpacking and extracting the length, sequence number and type from the received data. lenght, seq_type, data = struct.unpack('!HH%is' % (len(datagram) - 4), datagram) seql = seq_type >> 4 typ = seq_type & 15 offset = 4 #if type of the received data is not an ACK, then it has to send an ACK for the received data. if (typ != types["Acquittement"]): sequ_type = (seql << 4) | (types["Acquittement"]) buf = struct.pack('!HH', 4, sequ_type) self.transport.write(buf, host_port) print("send ack {}".format(buf)) self.seql += 1 #retransmission cancelled if ACK is received elif (typ == types["Acquittement"]): if self.repeat != None: self.repeat.cancel() self.repeat = None self.count = 0 self.seql += 1 if (typ == types["Refus connexion"]): self.clientProxy.connectionRejectedONE("user name already exists") #if received data is movielist then unpack and store it in the variable as defined. if (typ == types["Envoi liste films"]): while offset < lenght: print(offset, lenght) cip, port, lenmovie, iid = struct.unpack_from( '!IHHB', datagram, offset) offset += 9 movietitle = struct.unpack_from('!%is' % (lenmovie - 9), datagram, offset)[0].decode('utf-8') offset += lenmovie - 9 aip = str(ipaddress.IPv4Address(cip)) stport = str(port) stiid = str(iid) self.movielist.append((movietitle, aip, stport, stiid)) #if received data is userlist then unpack and store it in the variable as defined. if (typ == types["Envoi liste users"]): userList = [] while offset < lenght: lenuser, userstatus = struct.unpack_from( '!BB', datagram, offset) offset += 2 use = struct.unpack_from('!%is' % lenuser, datagram, offset)[0].decode('utf-8') offset += lenuser #stuserstatus=str(userstatus) if (userstatus == 0): auserstatus = ROOM_IDS.MAIN_ROOM else: for movie in self.movielist: if movie[3] == str(userstatus): auserstatus = movie[0] userList.append((use, auserstatus)) if self.userlist == []: self.userlist = userList self.clientProxy.initCompleteONE(self.userlist, self.movielist) else: self.userlist = userList self.clientProxy.setUserListONE(self.userlist) if (typ == types["Acquittement"]): if (seql == self.ackcm): self.clientProxy.joinRoomOKONE() if (seql == self.ackleave): self.clientProxy.leaveSystemOKONE() if (typ == types["Message Chat"]): lenuser = struct.unpack_from('!B', datagram, 4)[0] username = struct.unpack_from('!%is' % lenuser, datagram, 5)[0].decode('utf-8') lengthmsg = lenght - 4 - 1 - lenuser msg = struct.unpack_from('!%is' % lengthmsg, datagram, 5 + lenuser)[0].decode('utf-8') if (self.username != username): self.clientProxy.chatMessageReceivedONE(username, msg)
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr #dictionaire pour les id des films self.dictmoovie = dict() self.seqnum = 0 self.Token = 0 self.seqgtr = 0 self.deconnexion = 4 self.nombre_envoie = 0 self.reception_ack = {} def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def numseq(self, seq): #on genere un numero de sequence pour chaque paquet envoyé # en augmentant la valeur recue pour le prochain paquet # une fois le numero de sequence a 65536 on le reinitialise if seq == 65536: seq = 0 else: seq += 1 return seq #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est de type login reponse on teste la valeur de coderep def Preparepaquet(self, Version, Type, Token, Sequencenumber, payload): payloadsize = len(payload) header1 = (Version << 28) + (Type << 24) + Token header2 = (Sequencenumber << 16) + payloadsize packet = struct.pack('>II' + str(len(payload)) + 's', header1, header2, payload.encode('utf−8')) return (packet) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est de type login reponse on teste la valeur de coderep def PaquetLRQ(self, Version, Type, Token, Sequencenumber, iduser, username): usernamepack = struct.pack( '>HH' + str(len(username.encode('utf−8'))) + 's', iduser, len(username.encode('utf−8')), username.encode('utf−8')) userdatasize = len(usernamepack) header1 = (Version << 28) + (Type << 24) + Token header2 = (Sequencenumber << 16) + userdatasize packet = struct.pack('>II', header1, header2) + usernamepack return (packet) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est de type login reponse on teste la valeur de coderep # pour recevoir les paquets simples avec payload non decodé def paquetsimple(self, datagram): (header1, header2, payload) = struct.unpack('>II' + str(len(datagram) - 8) + 's', datagram) token = header1 & int('111111111111111111111111', 2) header1 = header1 >> 24 Type = header1 & int('1111', 2) header1 = header1 >> 4 version = header1 payloadsize = header2 & int('1111111111111111', 2) header2 = header2 >> 16 seqnum = header2 & int('1111111111111111', 2) return (version, Type, token, seqnum, payloadsize, payload) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est de type login reponse on teste la valeur de coderep def paquetrecuLRP(self, datagram): (header1, header2, coderp, userid, username) = struct.unpack('>IIBH' + str(len(datagram) - 11) + 's', datagram) token = header1 & int('111111111111111111111111', 2) header1 = header1 >> 24 Type = header1 & int('1111', 2) header1 = header1 >> 4 version = header1 payloadsize = header2 & int('1111111111111111', 2) header2 = header2 >> 16 seqnum = header2 & int('1111111111111111', 2) return (version, Type, token, seqnum, payloadsize, coderp, userid, username) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est de type login reponse on teste la valeur de coderep def constructack(self, Version, Type, Token, Sequencenumber, payload): payloadsize = len(payload) header1 = (Version << 28) + (Type << 24) + Token header2 = (Sequencenumber << 16) + payloadsize packet = struct.pack('>II' + str(len(payload)) + 's', header1, header2, payload.encode('utf−8')) return (packet) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def paquetGTR(self, Token, Seqnumber, moovie_id): header1 = (1 << 28) + (5 << 24) + Token payload = struct.pack('>H', moovie_id) gtrdatasize = len(payload) header2 = (Seqnumber << 16) + gtrdatasize packet = struct.pack('>II', header1, header2) + payload return (packet) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #fonnction de depaquetage lors de la reception de RST #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def depaquetageRST(self, datagram): # affiche le rst recu print(" ".join("{:02x}".format(c) for c in datagram)) # liste contenant la liste des utilisateurs dans la mainroom userList = list() # liste contenant le nom, port et l'ip du film movieList = list() # dictionnaire contenant les id de chaque film en fonction de leur nom user_moovielist = dict() # liste contenant l'id, port et l'ip du film userdata = list() datagram = datagram[8:] print(datagram) id_mainroom = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] taille = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] mainroom = struct.unpack(str(taille) + 's', datagram[0:taille]) mainroom = mainroom[0].decode('utf-8') mainroom = ROOM_IDS.MAIN_ROOM datagram = datagram[taille:] # récupere l'adresse ip et le port de la main room ip = struct.unpack('>I', datagram[0:4])[0] ip3 = ip & 255 ip = ip >> 8 ip2 = ip & 255 ip = ip >> 8 ip1 = ip & 255 ip = ip >> 8 ip0 = ip ip_adress = str.join(".", (str(ip0), str(ip1), str(ip2), str(ip3))) datagram = datagram[4:] Port = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] nombre_usermainroom = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] user_moovielist[mainroom] = id_mainroom #parcours des utilisateurs dans la main room for i in range(nombre_usermainroom): userId = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] userNameLength = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] userName = struct.unpack( str(userNameLength) + 's', datagram[0:userNameLength])[0] userName = userName.decode('utf-8') datagram = datagram[userNameLength:] userList.append((userName, mainroom)) userdata.append((userId, userName)) nbre_film = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] # parcours de la liste des films pour recuperer les utilisateurs regardant un film for j in range(nbre_film): id_moovieroom = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] taille = struct.unpack('>H', datagram[0:2])[0] print('taille du nom de film:' + str(taille)) datagram = datagram[2:] moovie_name = struct.unpack(str(taille) + 's', datagram[0:taille])[0] moovie_name = moovie_name.decode('utf-8') datagram = datagram[taille:] # récuperer l'adresse ip et le port de chaque film ip = struct.unpack('>I', datagram[0:4])[0] ip3 = ip & 255 ip = ip >> 8 ip2 = ip & 255 ip = ip >> 8 ip1 = ip & 255 ip = ip >> 8 ip0 = ip ip_adress = str.join(".", (str(ip0), str(ip1), str(ip2), str(ip3))) datagram = datagram[4:] Port = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] nmbre_usermoovieroom = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] # recupere la liste des utilisateurs regardant un film donné for i in range(nmbre_usermoovieroom): userId = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] taille = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] userName = struct.unpack( str(taille) + 's', datagram[0:taille])[0] userName = userName.decode('utf-8') print(' user pour cette room:' + userName) datagram = datagram[taille:] userList.append((userName, moovie_name)) userdata.append((userId, userName)) liste_vide = struct.unpack('>H', datagram[0:2])[0] datagram = datagram[2:] # liste contenant le nom, port et l'ip du film movieList.append((moovie_name, ip, Port)) # dictionnaire contenant les id de chaque film en fonction de leur nom user_moovielist[moovie_name] = id_moovieroom return (userList, movieList, user_moovielist, userdata) def message(self, Token, Seqnumber, id_user, message): id_user = struct.pack('>H', id_user) message = struct.pack('>H' + str(len(message.encode('utf−8'))) + 's', len(message.encode('utf−8')), message.encode('utf−8')) payload = id_user + message msgdatasize = len(payload) header1 = (1 << 28) + (6 << 24) + Token header2 = (Seqnumber << 16) + msgdatasize packet = struct.pack('>II', header1, header2) + payload return (packet) def leaveroom(self, Token, Sequencenumber): header1 = (1 << 28) + (7 << 24) + Token header2 = (Sequencenumber << 16) + 0 packet = struct.pack('>II', header1, header2) return (packet) def sendAndWait(self, paquet, numseq, host_port): #on verifie si le nombre d'envoie est inferieur a 3 et si on a recu un ack du client #si non on incremente le nombre d'envoie et si oui le paquet est aquité sinon #apres trois tentative on arrete d'envoyer le LRP if self.nombre_envoie <= 2 and self.reception_ack[numseq] == False: self.transport.write(paquet, host_port) self.nombre_envoie += 1 print('nmbre envoielrp:' + str(self.nombre_envoie)) reactor.callLater(1, self.sendAndWait, paquet, numseq, host_port) elif (self.reception_ack[numseq] == True): print('paquet aquitte') else: print('nmbre envoie:' + str(self.nombre_envoie)) if (self.nombre_envoie > 2): print('paquet perdu') def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) print(userName) datagram = self.PaquetLRQ(1, 1, 0, 0, 0, userName) print(datagram) print(" ".join("{:02x}".format(c) for c in datagram)) server = (self.serverAddress, self.serverPort) self.username = userName self.transport.write(datagram, server) self.reception_ack[self.seqnum] = False reactor.callLater(1, self.sendAndWait, datagram, self.seqnum, server) def sendChatMessageOIE(self, message): server = (self.serverAddress, self.serverPort) print('personne envoyant le message:' + str(self.userid)) seqnumber = self.numseq(int(self.seqnum)) datagram = self.message(self.Token, self.seqnum, self.userid, message) print(datagram) self.transport.write(datagram, server) self.reception_ack[self.seqnum] = False reactor.callLater(1, self.sendAndWait, datagram, self.seqnum, server) """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ server = (self.serverAddress, self.serverPort) seqnumber = self.numseq(int(self.seqnum)) self.seqgtr = self.seqnum print('seqgtr envoyé au serveur:' + str(self.seqgtr)) datagram = self.paquetGTR(self.Token, self.seqnum, self.dictmoovie[roomName]) print(datagram) self.transport.write(datagram, server) self.nombre_envoie = 0 self.reception_ack[self.seqnum] = False reactor.callLater(1, self.sendAndWait, datagram, self.seqnum, server) pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ server = (self.serverAddress, self.serverPort) seqnumber = self.numseq(int(self.seqnum)) self.deconnexion = seqnumber + 1 print('seqgtr envoyé au serveur:' + str(self.seqgtr)) datagram = self.leaveroom(self.Token, seqnumber + 1) print(datagram) self.transport.write(datagram, server) self.reception_ack[self.seqnum] = False reactor.callLater(1, self.sendAndWait, datagram, self.seqnum, server) pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ print('roomdata rst:' + str(datagram)) print(" ".join("{:02x}".format(c) for c in datagram)) #on decode le paquet recu du serveur (version, Type, Token, numseq, payloadsize, payload) = self.paquetsimple(datagram) #on numero de sequence courant recu self.seqnum = numseq self.Token = Token #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est ack on l'affiche if (Type == 0): self.reception_ack[numseq] = True print('ack serveur:' + str(version) + ',' + str(Type) + ',' + str(self.Token) + ',' + str(self.seqnum) + ',' + str(payloadsize) + ',' + str(payload)) if (self.seqgtr == self.seqnum): print('joinRoomOKONE') self.clientProxy.joinRoomOKONE() self.seqgtr = 0 elif (self.deconnexion == self.seqnum): self.clientProxy.leaveSystemOKONE() #(userList,moovieList,dictmovie)=self.depaquetageRST(datagram) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est de type login reponse on teste la valeur de coderep #si le paquet recu n'est pas un ack on envoie un ack au serveur #a revoir pour le cas ou type=2 on envoie rien if (Type != 0): print('seqnum venant du serveur:' + str(self.seqnum)) print('le message recu est pas un ack') ack = self.constructack(1, 0, self.Token, self.seqnum, '') self.transport.write(ack, host_port) print('ack bien envoyé au serveur') #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #si le paquet recu est de type login reponse on teste la valeur de coderep if (Type == 2): #(coderep,userid,username)=struct.unpack('>BH'+str((payload)-3)+'s',payload) (version, Type, self.Token, self.seqnum, payloadsize, coderp, self.userid, username) = self.paquetrecuLRP(datagram) print('coderep:' + str(coderp)) if (coderp == 1): erreur = 'username non conforme' self.clientProxy.connectionRejectedONE(erreur) elif (coderp == 2): erreur = 'username too long' self.clientProxy.connectionRejectedONE(erreur) elif (coderp == 3): erreur = 'username deja utilise' self.clientProxy.connectionRejectedONE(erreur) elif (coderp == 255): erreur = 'unknow error' self.clientProxy.connectionRejectedONE(erreur) #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #depaquetage de la userlist et de la moovielist #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if Type == 4: if self.seqnum == 1: (self.userList, self.moovieList, self.dictmoovie, self.userdata) = self.depaquetageRST(datagram) self.clientProxy.initCompleteONE(self.userList, self.moovieList) elif self.seqnum > 1: (self.userList, self.moovieList, self.dictmoovie, self.userdata) = self.depaquetageRST(datagram) self.clientProxy.setUserListONE(self.userList) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # pour mettre a jour la liste des utilisateurs quand une nouvelle personne #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (Type == 6): (header1, header2, id_user, usernamesize, message) = struct.unpack('>IIHH' + str(len(datagram) - 12) + 's', datagram) for user in self.userdata: if (user[0] == id_user): print('user envoyant le message trouvé avec id:', str(user[0]), str(user[1])) self.clientProxy.chatMessageReceivedONE( user[1], message.decode('utf-8')) pass
class c2wUdpChatClientProtocol(DatagramProtocol): seq = 1 lastMessage = '' attenteServer = 0 #Initié à 0 pour le premier envoie de login ACKServer = 0 waitForACK = '' def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort self.serverHost = serverAddress, serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) userName = userName.encode('utf8') self.attenteServer = 0 self.ACKServer = 0 self.sendLogin(userName) def sendLogin(self, userName): if ((self.attenteServer < 10) and (self.ACKServer == 0)): if self.attenteServer != 0: self.seq = 1 self.sender(len(userName), 1, userName, self.serverHost) self.waitForACK = reactor.callLater(1, self.sendLogin, userName) self.attenteServer += 1 elif self.attenteServer == 10: print("The server do not answer") def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ taille = len(message) if taille * 2 < 65000: message = message.encode('utf-8') self.attenteServer = 0 self.ACKServer = 0 self.sendChatMessage(message) def sendChatMessage(self, message): if ((self.attenteServer < 10) and (self.ACKServer == 0)): self.sender(len(message), 5, message, self.serverHost) self.waitForACK = reactor.callLater(1, self.sendChatMessage, message) self.attenteServer += 1 elif self.attenteServer == 10: print("The server do not answer") def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ if not isinstance(roomName, str): idm = 0 else: idm = movieIds[roomName] msg = bytearray(1) struct.pack_into('!b', msg, 0, idm) self.attenteServer = 0 self.ACKServer = 0 self.sendJoinRoom(msg) def sendJoinRoom(self, msg): if ((self.attenteServer < 10) and (self.ACKServer == 0)): self.sender(1, 6, msg, self.serverHost) self.waitForACK = reactor.callLater(1, self.sendJoinRoom, msg) self.attenteServer += 1 elif self.attenteServer == 10: print("The server does not answer") def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ self.attenteServer = 0 self.ACKServer = 0 self.sendLeaveSystem() def sendLeaveSystem(self): if ((self.attenteServer < 10) and (self.ACKServer == 0)): self.lastMessage = 'quit' self.sender(0, 9, None, self.serverHost) self.waitForACK = reactor.callLater(1, self.sendLeaveSystem) self.attenteServer += 1 elif self.attenteServer == 10: print("The server does not answer") def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ global movieList global userList global movieIds msg = struct.unpack_from('!hh' + str(len(datagram) - 4) + 's', datagram) taille = msg[0] st = msg[1] #Seq + Type if taille == len(datagram): seqType = self.getSeqType( st) #Tupple contenant la Sequence et le Type (nseq, tTrame) = seqType if seqType[ 1] != 63: #Si le message reçu n'est pas un ACK, on envoie un ACK print('msg received -- ack sent response to ' + str(seqType[1])) self.sendAck(seqType[0], host_port) if seqType[1] == 8: #REFUSEE print('inscription refused') datagram = datagram[4:] check = struct.unpack_from('b', datagram) self.clientProxy.connectionRejectedONE(str(check[0])) if seqType[1] == 11: self.clientProxy.joinRoomOKONE() if seqType[1] == 2: #MOVIES print('movie list received') movieList = [] movieIds = {} datagram = datagram[4:] while len(datagram) != 0: tp = struct.unpack_from('b', datagram) #on recup la taille form = 'bbbbb' t = struct.unpack_from(form, datagram) ipl = t[1:5] #recuperation de l ip ip = '' for i in range(4): #boucle de reconcatenation de l ip ip += str(ipl[i]) + '.' ip = ip[:-1] # enlever le dernier point datagram = datagram[5:] te = struct.unpack_from('!hb' + str(tp[0] - 8) + 's', datagram) movieList += [(te[2].decode('utf-8'), ip, te[0])] movieIds[te[2].decode('utf-8')] = te[1] datagram = datagram[tp[0] - 5:] if seqType[1] == 3: #USERS print('user list received') userList = [] datagram = datagram[4:] while len(datagram) != 0: tp = struct.unpack_from('b', datagram) #on recup la taille form = 'bb' + str(tp[0] - 2) + 's' t = struct.unpack_from(form, datagram) user = t[2].decode('utf-8') #user=user[4:] if t[1] == 0: userList += [(user, ROOM_IDS.MAIN_ROOM)] else: userList += [(user, t[1])] datagram = datagram[tp[0]:] self.clientProxy.initCompleteONE(userList, movieList) if seqType[1] == 10: #Message reçu print('message received') tailleUser = struct.unpack_from('!b', msg[2]) form = '!b' + str(tailleUser[0]) + 's' + str( len(msg[2]) - 1 - tailleUser[0]) + 's' umsg = struct.unpack_from(form, msg[2]) usr = umsg[1].decode('utf-8') msg = umsg[2].decode('utf-8') self.clientProxy.chatMessageReceivedONE(usr, msg) if seqType[1] == 63: #ACK print('ack received') print(nseq, self.seq) if (nseq + 1 == self.seq): if ( self.ACKServer == 0 ): #Si on attendait un ACK de l'utilisateur, on cancel le bouclage du racteur self.waitForACK.cancel( ) #On cancel le bouclage du Send&Wait self.ACKServer = 1 if self.lastMessage == 'quit': print('user left') self.clientProxy.leaveSystemOKONE() #else: if seqType[1] == 8: #REFUS INSCRIPTION print('Inscription refused') print(datagram) if seqType[1] == 4: #MISE A JOUR UTILISATEUR datagram = datagram[4:] [idSalon, userName ] = struct.unpack_from('b' + str(len(datagram) - 1) + 's', datagram) userName = userName.decode('utf8') print('user updated') if idSalon == 127: self.clientProxy.userUpdateReceivedONE( userName, ROOM_IDS.OUT_OF_THE_SYSTEM_ROOM) elif idSalon == 0: self.clientProxy.userUpdateReceivedONE( userName, ROOM_IDS.MAIN_ROOM) else: roomName = list(movieIds.keys())[list( movieIds.values()).index(idSalon)] print(roomName) self.clientProxy.userUpdateReceivedONE(userName, roomName) def sendAck(self, seq, host_port): typeM = self.dec2bin(63, 6) seqM = self.dec2bin(seq, 10) st = seqM + typeM msg = bytearray(4) struct.pack_into('!hh', msg, 0, 4, int(st, 2)) self.transport.write(msg, host_port) def dec2bin(self, decimal, nbbits): if decimal == 0: return "0".zfill(nbbits) result = "" while decimal != 0: decimal, rest = divmod(decimal, 2) result = "01"[rest] + result return result.zfill(nbbits) #RETOURNE lE NUM DE SEQ ET LE TYPE DE TRAME def getSeqType(self, st): st = self.dec2bin(st, 16) seq = st[:10] types = st[10:16] return int(seq, 2), int(types, 2) #tupple[0] = sequence, tupple[1] = type def sender(self, taille, typeM, data, host_port): typeM = self.dec2bin(typeM, 6) #conversion du type sur 6 bit seqM = self.dec2bin(self.seq, 10) #conversion seq sur 6 bit self.seq += 1 st = seqM + typeM #on concatene sequence et type buf = bytearray(4) struct.pack_into('!hh', buf, 0, taille + 4, int(st, 2)) if data != None: buf = buf + data self.transport.write(buf, host_port)
class c2wUdpChatServerProtocol(DatagramProtocol): def __init__(self, serverProxy, lossPr): print('begin the initilization of this class') """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr self.ackReceived = {} self.num = 0 #iniitilization of sequence number is 0 self.online = 0 #self.online: the number of users in the server, to ensure if the server is sature #test correct:self.resend = None self.tryTime = {} self.numAttendu = {} self.resend = None self.isSend = {} self.wasReceived = True print('firstly show the movie list', self.serverProxy.getMovieList) self.movieList = {} for m in self.serverProxy.getMovieList(): print("Movie title: ", m.movieTitle, m.movieId) print('ip', self.serverProxy.getMovieAddrPort(m.movieTitle)[0]) print('port', self.serverProxy.getMovieAddrPort(m.movieTitle)[1]) self.serverProxy.startStreamingMovie(m.movieTitle) #begin the streaming in all movie rooms #heer can be better: if this movie room is no person, we can stop the streaming self.movieList[m.movieTitle] = [0, []] print('movie list is ', self.movieList) print('test user list in server', self.serverProxy.getUserList()) print('write a fonction which starts when the procedure is lanced') self.list = {} ticks = time.time() print('for thesfdfdsd present time is:', ticks) def numConstruct(self): #each time before we send a datagram need to generate a number sequence # and agument self.num which means the next number seq ready to be sent # ensure the number returns to 0 when it arrives at 2047 numSequence = self.num if self.num == 2047: self.num = 0 else: self.num += 1 return numSequence #okay def testUsername(self, username): #param: username in format string #in spec we know that the format of username need to be: #1. the length is not over 255 caracteres #2. it begin by a alphabet and maybe some alphabet/numbers if (len(username) > 256): print('the length of username is too long so that no valid') return false else: if not (username[0].isalpha()): print('the beginning of username is not an alphabet') return False else: if not (username[1:].isalnum()): print('the following string of username isnot num/alpha ') return False else: return True def sendMovieRoom(self, host_port): NumSalonP = 0 salonP = [] salonList = {} corps0 = b'' for u in self.serverProxy.getUserList(): print(u) if (u.userChatRoom == ROOM_IDS.MAIN_ROOM): print('this user is in the main room') NumSalonP += 1 salonP.append(u.userName) else: print('gergze', u.userChatRoom) print(self.movieList) if u.userChatRoom in self.movieList: self.movieList[u.userChatRoom][0] += 1 self.movieList[u.userChatRoom][1].append(u.userName) corps0 += struct.pack('>H', NumSalonP) print('exam for movie list: slef', self.movieList) for i in salonP: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) for i in self.movieList: corps0 += struct.pack('>H', self.movieList[i][0]) if not (self.movieList[i][1] == 0): print('this moivie room is not empty') for i in self.movieList[i][1]: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) print(corps0, 'corps0 check itself') TypeBin = bin(19) Type0 = TypeBin[2:].rjust(8, "0") numSequence = self.num self.num += 1 NumSeqBin = bin(numSequence) NumSeqUserList = NumSeqBin[2:].rjust(10, "0") LongueurInt = 1 + len(corps0) + 1 LongueurBin = bin(LongueurInt) Longueur0 = LongueurBin[2:].rjust(14, "0") #datagram_UserList=struct.pack('>IH',int(Type0+NumSeqUserList+Longueur0,2),numSalon)+corps0 datagram_UserList = struct.pack( '>IH', int(Type0 + NumSeqUserList + Longueur0, 2), 2) + corps0 self.transport.write(datagram_UserList, host_port) print('datagram of user list', datagram_UserList) movieListDatagram = self.movieListDatagram() self.sendDatagram(1, len(movieListDatagram), movieListDatagram, host_port) def sendAndWait(self, datagram, numSeq, host_port): print('12') print(numSeq, datagram, host_port) if (self.wasReceived): reactor.callLater(5, self.sendAndWait, datagram, numSeq, host_port) else: if self.tryTime[numSeq] <= 4 and self.ackReceived[numSeq] == False: print('send agaim') self.transport.write(datagram, host_port) print('resend the datagram', datagram) self.tryTime[numSeq] += 1 self.resend[numSeq] = reactor.callLater( 5, self.sendAgain, datagram, numSeq, host_port) else: if (self.ackReceived[numSeq] == True): self.wasReceived = True print('this datagram has been responded', datagram) elif (self.tryTime[numSeq] > 4): print('time our for the datagram\'s ack', datagram) userToDelete = self.serverProxy.getUserByAddress(host_port) def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport nomMovie = 0 for m in self.serverProxy.getMovieList(): nomMovie += 1 print("Movie title: ", m.movieTitle, m.movieId) print('ip port', self.serverProxy.getMovieAddrPort(m.movieTitle)[0]) print(nomMovie) def checkAck(self, host_port): print('now we are testing the ack') delay(1000) print('the ack is not received within 1000') return 0 ''' def numConstruct(self): numSequence=self.num self.num+=1 return numSequence ''' def getTypeFromDatagram(self, datagram): #typeDatagram: int typeDatagram = struct.unpack('b', datagram[0:1])[0] return typeDatagram def getNumSeqFromDatagram(self, datagram): #numSequence: int unpack = struct.unpack('>I', datagram[0:4]) print('unpack', bin(unpack[0])[2:].rjust(32, '0')[-24:-14]) numSeq = int(bin(unpack[0])[2:].rjust(32, '0')[-24:-14], 2) return numSeq def ackConstructor(self, datagram): typeAck = 80 dataHead = struct.unpack('>I', datagram[0:4]) #split the number of sequence in this datagram numSeq = bin(dataHead[0])[2:].rjust(32, '0')[-24:-14] print(numSeq, 'number of seq') lengthAck = bin(0)[2:].rjust(14, "0") print(bin(typeAck)[2:].rjust(8, "0"), numSeq, lengthAck, 'check ack') ackInt = int(bin(typeAck)[2:].rjust(8, "0") + numSeq + lengthAck, 2) ack = struct.pack('>I', ackInt) return ack def getNumSeqFromAck(self, ack): #split the number of sequence from ack datagram unpack = struct.unpack('>I', ack) numSeq = int(bin(unpack[0])[-24:-14], 2) return numSeq def connFailedConstr(self): typeBin = bin(2)[2:].rjust(8, "0") numSequence = self.numConstruct() numSeqBin = bin(numSequence)[2:].rjust(10, "0") lengthBin = bin(0)[2:].rjust(14, "0") datagram = struct.pack('>I', int(typeBin + numSeqBin + lengthBin, 2)) numSeq = int(numSeqBin, 2) return numSeq, datagram def errorConstr(self, error): typeBin = bin(128)[2:].rjust(8, "0") numSequence = self.numConstruct() numSeqBin = bin(numSequence)[2:].rjust(10, "0") lengthBin = bin(len(error) + 1)[2:].rjust(14, "0") head = typeBin + numSeqBin + lengthBin datagram = struct.pack('>Ib%ds' % len(error), int(head, 2), len(error), error.encode('utf-8')) numSeq = int(numSeqBin, 2) return numSeq, datagram def movieListDatagram(self): self.numMovie = 0 corpsMovie = b'' for m in self.serverProxy.getMovieList(): ip = self.serverProxy.getMovieAddrPort(m.movieTitle)[0] ip = re.findall(r"\d+", ip) print('show me the ip after process', ip) port = self.serverProxy.getMovieAddrPort(m.movieTitle)[1] print('show me the port number ', port) port = bin(port) port = port[2:].rjust(14, "0") movieName = m.movieTitle movieName = movieName.encode("utf-8") print('shoe me the length of movie name', len(movieName), type(len(movieName))) #corps+=struct.pack('b',len(movieName)) corpsMovie += struct.pack('b%ds4b' % len(movieName), len(movieName), movieName, int(ip[0]), int(ip[1]), int(ip[2]), int(ip[3])) corpsMovie += struct.pack('>H', int(port, 2)) self.numMovie += 1 movieListDatagram = struct.pack('>H', self.numMovie) + corpsMovie return movieListDatagram def sendAgain(self, datagram, numSeq, host_port): print('12') print(numSeq, datagram, host_port) if self.tryTime[numSeq] <= 4 and self.ackReceived[numSeq] == False: print('send agaim') self.transport.write(datagram, host_port) print('resend the datagram', datagram) self.tryTime[numSeq] += 1 reactor.callLater(5, self.sendAgain, datagram, numSeq, host_port) else: if (self.ackReceived[numSeq] == True): print('this datagram has been responded', datagram) elif (self.tryTime[numSeq] > 4): print('time our for the datagram\'s ack', datagram) def sendDatagram(self, typeDatagram, longueur, Corps, host_port): typeDatagramBin = bin(typeDatagram)[2:].rjust(8, "0") numSeqInt = self.numConstruct() numSeqBin = bin(numSeqInt)[2:].rjust(14, '0') longueurBin = bin(longueur)[2:].rjust(10, "0") print(typeDatagramBin + numSeqBin + longueurBin, 'test in senddatagram') datagram = struct.pack( '>I', int(typeDatagramBin + numSeqBin + longueurBin, 2)) + Corps self.transport.write(datagram, host_port) def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ print('receice a datagram from', datagram, host_port) #here if need a test for mal format message typeDatagram = self.getTypeFromDatagram(datagram) print('receice a datagram from', datagram, host_port, 'at the type of:', typeDatagram) #except we receive an acquitement, we must send back a message of acquitement numReceived = self.getNumSeqFromDatagram(datagram) if (typeDatagram == 80): print('now the server receive an ack from client:', datagram) numSeqAck = self.getNumSeqFromAck(datagram) print('the number of sequence ack is:', numSeqAck) self.ackReceived[numSeqAck] = True if not host_port in self.isSend: self.isSend[host_port] = True self.numAttendu[host_port] = 0 if (typeDatagram != 80): ''' if not (numReceived==self.numAttendu[host_port]): print(numReceived,self.numAttendu) print('the number sequence received is not the num seq wait') numSeq,errorDatagram=self.errorConstr('Numero de sequence inattendu') print('send tht datagram of error:',errorDatagram) self.ackReceived[numSeq]=False self.transport.write(errorDatagram,host_port) return #if the number of sequence is not correct, ignore this message ''' self.numAttendu[host_port] += 1 ack = self.ackConstructor(datagram) print('send an ack:', ack, 'to be a response of datagram:', datagram) self.transport.write(ack, host_port) if (typeDatagram == 0): print('this is a request of connection to server') print(datagram[5:]) print(len(datagram[5:])) userName = struct.unpack('%ds' % len(datagram[5:]), datagram[5:])[0].decode("utf-8") userNameLength = len(userName) userNameComplete = str(userNameLength) + userName print('THE INPUT USERNAME IS :', userName, 'username complete', userNameComplete) if (self.online >= 512): print( 'now the server is sature and no longer resource for a new user' ) numSeq, datagram = self.connFailedConstr() self.transport.write(datagram, host_port) self.ackReceived[numSeq] = False print('send a datagtam refuse', datagram) print('set self.ackReceived:', numSeq, 'to false') numSeq, errorDatagram = self.errorConstr('Serveur sature') print('send tht datagram of error:', errorDatagram) self.ackReceived[numSeq] = False self.transport.write(errorDatagram, host_port) print('set self.ackReceived:', numSeq, 'to false') elif (self.testUsername(userName) == False): print('this username is not in the correct format') numSeq, datagram = self.connFailedConstr() self.transport.write(datagram, host_port) self.ackReceived[numSeq] = False print('send a datagtam refuse', datagram) print('set self.ackReceived:', numSeq, 'to false') numSeq, errorDatagram = self.errorConstr( 'Mauvais nom d’utilisateurur') print('send tht datagram of error:', errorDatagram) self.ackReceived[numSeq] = False self.transport.write(errorDatagram, host_port) typeDatagram = struct.unpack('>b', errorDatagram[0:1]) print(typeDatagram[0], 'typeDatagram errror', errorDatagram) print('set self.ackReceived:', numSeq, 'to false') elif (self.serverProxy.userExists(userName)): print( 'this username is already in use and existing user list:', self.serverProxy.getUserList()) numSeq, datagram = self.connFailedConstr() self.transport.write(datagram, host_port) self.ackReceived[numSeq] = False print('send a datagtam refuse', datagram) print('set self.ackReceived:', numSeq, 'to false') numSeq, errorDatagram = self.errorConstr( 'Utilisateur deja existant') print('send tht datagram of error:', errorDatagram) self.ackReceived[numSeq] = False self.transport.write(errorDatagram, host_port) print('set self.ackReceived:', numSeq, 'to false') else: print('this user can be allowed in') print('test the user list:', self.serverProxy.getUserList()) self.online += 1 #this username is admitted print('username valid') TypeBin = bin(1) Type = TypeBin[2:].rjust(8, "0") numSequence = self.numConstruct() NumSeqBin = bin(numSequence) NumSeq = NumSeqBin[2:].rjust(10, "0") LongueurInt = 0 LongueurBin = bin(LongueurInt) Longueur = LongueurBin[2:].rjust(14, "0") print(Type + NumSeq + Longueur, 'datagram beform packing') datagram = struct.pack('>I', int(Type + NumSeq + Longueur, 2)) self.transport.write(datagram, host_port) self.tryTime[numSequence] = 0 self.ackReceived[numSequence] = False #CORRECT:self.resend = reactor.callLater(5,self.sendAndWait,datagram,numSequence,host_port) $ print('check resend', self.resend) self.resend = {} self.resend[numSequence] = None self.resend[numSequence] = reactor.callLater( 5, self.sendAgain, datagram, numSequence, host_port) print(self.tryTime, self.resend) for i in self.serverProxy.getUserList(): print(i, 'test all user in the list 1420') print('final test for user list', self.serverProxy.getUserList()) self.serverProxy.addUser(userName, ROOM_IDS.MAIN_ROOM, None, host_port) #self.serverProxy.addUser('linue', 'Great Guy') print('check if user is add to the list', self.serverProxy.getUserList()) #now we send a datagram of movie list to client #numSalon initial 1 cause we must have a main room NumSalonP = 0 salonP = [] salonList = {} corps0 = b'' print('test for user list change while a new user', self.serverProxy.getUserList()) ''' [<Instance of c2wUser; userName=alice, userChatRoom=<class 'c2w.main.constants.ROOM_IDS.MAIN_ROOM'>, userChatInstance=None, userAddress=('127.0.0.1', 46602)>, <Instance of c2wUser; userName=aliceff, userChatRoom=<class 'c2w.main.constants.ROOM_IDS.MAIN_ROOM'>, userChatInstance=None, userAddress=('127.0.0.1', 36153)>] ''' self.movieList0 = {} for m in self.serverProxy.getMovieList(): self.movieList0[m.movieTitle] = [0, []] for u in self.serverProxy.getUserList(): print(u) if (u.userChatRoom == ROOM_IDS.MAIN_ROOM): print('this user is in the main room') NumSalonP += 1 salonP.append(u.userName) else: if u.userChatRoom in self.movieList: self.movieList0[u.userChatRoom][0] += 1 self.movieList0[u.userChatRoom][1].append( u.userName) self.movieList = self.movieList0 corps0 += struct.pack('>H', NumSalonP) print('test salonP', salonP) print('exam for movie list: slef', self.movieList) for i in salonP: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) for i in self.movieList: corps0 += struct.pack('>H', self.movieList[i][0]) if not (self.movieList[i][1] == 0): print('this moivie room is not empty') for i in self.movieList[i][1]: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) print(corps0, 'corps0 check itself') TypeBin = bin(19) Type0 = TypeBin[2:].rjust(8, "0") Type0 = TypeBin[2:].rjust(8, "0") numSequence = self.num self.num += 1 NumSeqBin = bin(numSequence) NumSeqUserList = NumSeqBin[2:].rjust(10, "0") LongueurInt = 1 + len(corps0) + 1 LongueurBin = bin(LongueurInt) Longueur0 = LongueurBin[2:].rjust(14, "0") #datagram_UserList=struct.pack('>IH',int(Type0+NumSeqUserList+Longueur0,2),numSalon)+corps0 datagram_UserList = struct.pack( '>IH', int(Type0 + NumSeqUserList + Longueur0, 2), len(self.movieList) + 1) + corps0 #self.transport.write(datagram_UserList,host_port) sendList = [] for u in self.serverProxy.getUserList(): print(u.userChatRoom) if (u.userChatRoom == ROOM_IDS.MAIN_ROOM): sendList.append(u.userAddress) print('send list check', sendList) if (len(sendList) > 0): for i in sendList: self.transport.write(datagram_UserList, i) print('datagram of user list', datagram_UserList) movieListDatagram = self.movieListDatagram() self.sendDatagram(16, len(movieListDatagram), movieListDatagram, host_port) #corps+=struct.pack('b%ds4b>H'%len(movieName),len(movieName),movieName,ip[0],ip[1],ip[2],ip[3],int(port,2)) #movieList.apppend((m.movieTitle,self.serverProxy.getMovieAddrPort(m.movieTitle)[0],self.serverProxy.getMovieAddrPort(m.movieTitle)[1])) ''' print('add a user in the list of user') start_end[numSequence]=timeit.timeit() print('test the start time of waiting an ack',start_end[numSequence], start_end) print('room ids ',ROOM_IDS.MAIN_ROOM) #now we need to send a datagram about movie list to client self.transport.write(movieList,host_port) for m in self.serverProxy.getMovieList(): print('get movie list',m.movieId) for u in self.serverProxy.getUserList(): print('user name: ', u.userName,u.userChatRoom) if(u.userChatRoom==ROOM_IDS.MAIN_ROOM): print('okay') if not(u.userChatRoom==ROOM_IDS.MOVIE_ROOM): print(' not okay') #if this datagram for connection is not received by our client #this means the ack from client is not received here''' elif (typeDatagram == 48 or typeDatagram == 49): print( 'now the server receives an request of entring/quitting the video room', typeDatagram) typeAck = 80 # numSeq=datagramSplit(datagram) dataHead = struct.unpack('>I', datagram[0:4]) #print(dataHead,'datahead') numSequenceBin = (bin(dataHead[0])[2:].rjust(32, '0')[8:18]) numSeq = numSequenceBin longueur = bin(0) longueurAck = longueur[2:].rjust(14, "0") ack0bin = bin(typeAck)[2:].rjust(8, "0") + numSeq + longueurAck ack0 = int(ack0bin, 2) ack = struct.pack('>I', ack0) self.transport.write(ack, host_port) print('send an ack for entring the video room', ack) if (typeDatagram == 48): lenroomName = datagram[4] roomName = struct.unpack('%ds' % lenroomName, datagram[5:])[0].decode('utf-8') #self.serverProxy.addUser(userName,ROOM_IDS.MOVIE_ROOM,None,host_port) print('now the user is in the room', roomName) print('show me the user List now', self.serverProxy.getUserList()) print( 'username to change its position', self.serverProxy.getUserByAddress(host_port).userName) self.serverProxy.updateUserChatroom( self.serverProxy.getUserByAddress(host_port).userName, roomName) print('show me the user List now WHEN I ENTER MOVIE ROOM', self.serverProxy.getUserList()) else: roomBefore = self.serverProxy.getUserByAddress( host_port).userChatRoom self.serverProxy.updateUserChatroom( self.serverProxy.getUserByAddress(host_port).userName, ROOM_IDS.MAIN_ROOM) print('show me the user List now WHEN I QUIT MOVIE ROOM', self.serverProxy.getUserList()) Type0 = bin(18)[2:].rjust(8, "0") numSequence = self.num self.num += 1 NumSeqBin = bin(numSequence) NumSeqUserList = NumSeqBin[2:].rjust(10, "0") corps = b'' sendListRoom = [] peopleRoom = [] for u in self.serverProxy.getUserList(): print(u.userChatRoom, roomBefore) if (u.userChatRoom == roomBefore): print('hi a friend in this movie room') sendListRoom.append(u.userAddress) peopleRoom.append(u.userName) if (len(peopleRoom) > 0): corps += struct.pack('>H', len(peopleRoom)) for i in peopleRoom: corps += struct.pack('b%ds' % len(i), len(i), i.encode('utf-8')) print('send list check', sendListRoom) LongueurInt = len(corps) LongueurBin = bin(LongueurInt) Longueur0 = LongueurBin[2:].rjust(14, "0") head = Type0 + NumSeqUserList + Longueur0 datagram = struct.pack('>I', int(head, 2)) + corps if (len(sendListRoom) > 0): print('send list to users in salon', datagram) for i in sendListRoom: self.transport.write(datagram, i) NumSalonP = 0 salonP = [] salonList = {} corps0 = b'' print('test for user list change while a new user', self.serverProxy.getUserList()) ''' [<Instance of c2wUser; userName=alice, userChatRoom=<class 'c2w.main.constants.ROOM_IDS.MAIN_ROOM'>, userChatInstance=None, userAddress=('127.0.0.1', 46602)>, <Instance of c2wUser; userName=aliceff, userChatRoom=<class 'c2w.main.constants.ROOM_IDS.MAIN_ROOM'>, userChatInstance=None, userAddress=('127.0.0.1', 36153)>] ''' self.movieList0 = {} for u in self.serverProxy.getUserList(): print(u) if (u.userChatRoom == ROOM_IDS.MAIN_ROOM): print('this user is in the main room') NumSalonP += 1 salonP.append(u.userName) else: for m in self.serverProxy.getMovieList(): self.movieList0[m.movieTitle] = [0, []] print('test userchatroom and movie list', u.userChatRoom, self.movieList) if u.userChatRoom in self.movieList: print('yes fina a user in movie room') self.movieList0[u.userChatRoom][0] += 1 self.movieList0[u.userChatRoom][1].append( u.userName) self.movieList = self.movieList0 corps0 += struct.pack('>H', NumSalonP) print('test salonP', salonP) print('exam for movie list: self', self.movieList) for i in salonP: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) for i in self.movieList: corps0 += struct.pack('>H', self.movieList[i][0]) if not (self.movieList[i][1] == []): print('this moivie room is not empty') for i in self.movieList[i][1]: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) print(corps0, 'corps0 check itself') TypeBin = bin(19) Type0 = TypeBin[2:].rjust(8, "0") numSequence = self.num self.num += 1 NumSeqBin = bin(numSequence) NumSeqUserList = NumSeqBin[2:].rjust(10, "0") LongueurInt = 1 + len(corps0) + 1 LongueurBin = bin(LongueurInt) Longueur0 = LongueurBin[2:].rjust(14, "0") #datagram_UserList=struct.pack('>IH',int(Type0+NumSeqUserList+Longueur0,2),numSalon)+corps0 datagram_UserList = struct.pack( '>IH', int(Type0 + NumSeqUserList + Longueur0, 2), len(self.movieList) + 1) + corps0 #self.transport.write(datagram_UserList,host_port) sendList = [] for u in self.serverProxy.getUserList(): print(u.userChatRoom) if (u.userChatRoom == ROOM_IDS.MAIN_ROOM): sendList.append(u.userAddress) print('send list check', sendList) if (len(sendList) > 0): for i in sendList: self.transport.write(datagram_UserList, i) print('datagram of user list for type 48 / 49', datagram_UserList) if (typeDatagram == 48): Type0 = bin(18)[2:].rjust(8, "0") numSequence = self.num self.num += 1 NumSeqBin = bin(numSequence) NumSeqUserList = NumSeqBin[2:].rjust(10, "0") corps = b'' sendListRoom = [] peopleRoom = [] for u in self.serverProxy.getUserList(): print(u.userChatRoom, roomName) if (u.userChatRoom == roomName): print('hi a friend in this movie room') sendListRoom.append(u.userAddress) peopleRoom.append(u.userName) if (len(peopleRoom) > 0): corps += struct.pack('>H', len(peopleRoom)) for i in peopleRoom: corps += struct.pack('b%ds' % len(i), len(i), i.encode('utf-8')) print('send list check', sendListRoom) LongueurInt = len(corps) LongueurBin = bin(LongueurInt) Longueur0 = LongueurBin[2:].rjust(14, "0") head = Type0 + NumSeqUserList + Longueur0 datagram = struct.pack('>I', int(head, 2)) + corps if (len(sendListRoom) > 0): print('send list to users in salon', datagram) for i in sendListRoom: self.transport.write(datagram, i) elif (typeDatagram == 3): print('now there is a user who wanna quit the system', host_port) #del self.numAttendu[host_port] self.serverProxy.removeUser( self.serverProxy.getUserByAddress(host_port).userName) #self.serverProxy.updateUserChatroom(self.serverProxy.getUserByAddress(host_port).userName,ROOM_IDS.OUT_OF_THE_SYSTEM_ROOM) self.online -= 1 print('self.online', self.online) print('show me the user List when someOne LEAVE', self.serverProxy.getUserList()) NumSalonP = 0 salonP = [] salonList = {} corps0 = b'' print('test for user list change while a new user', self.serverProxy.getUserList()) ''' [<Instance of c2wUser; userName=alice, userChatRoom=<class 'c2w.main.constants.ROOM_IDS.MAIN_ROOM'>, userChatInstance=None, userAddress=('127.0.0.1', 46602)>, <Instance of c2wUser; userName=aliceff, userChatRoom=<class 'c2w.main.constants.ROOM_IDS.MAIN_ROOM'>, userChatInstance=None, userAddress=('127.0.0.1', 36153)>] ''' self.movieList0 = {} for m in self.serverProxy.getMovieList(): self.movieList0[m.movieTitle] = [0, []] for u in self.serverProxy.getUserList(): print(u) if (u.userChatRoom == ROOM_IDS.MAIN_ROOM): print('this user is in the main room') NumSalonP += 1 salonP.append(u.userName) else: if u.userChatRoom in self.movieList: self.movieList0[u.userChatRoom][0] += 1 self.movieList0[u.userChatRoom][1].append( u.userName) self.movieList = self.movieList0 corps0 += struct.pack('>H', NumSalonP) print('test salonP', salonP) print('exam for movie list: slef', self.movieList) for i in salonP: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) for i in self.movieList: corps0 += struct.pack('>H', self.movieList[i][0]) if not (self.movieList[i][1] == 0): print('this moivie room is not empty') for i in self.movieList[i][1]: i = i.encode("utf-8") corps0 += struct.pack('b%ds' % len(i), len(i), i) print(corps0, 'corps0 check itself') TypeBin = bin(19) Type0 = TypeBin[2:].rjust(8, "0") Type0 = TypeBin[2:].rjust(8, "0") numSequence = self.num self.num += 1 NumSeqBin = bin(numSequence) NumSeqUserList = NumSeqBin[2:].rjust(10, "0") LongueurInt = 1 + len(corps0) + 1 LongueurBin = bin(LongueurInt) Longueur0 = LongueurBin[2:].rjust(14, "0") #datagram_UserList=struct.pack('>IH',int(Type0+NumSeqUserList+Longueur0,2),numSalon)+corps0 datagram_UserList = struct.pack( '>IH', int(Type0 + NumSeqUserList + Longueur0, 2), len(self.movieList) + 1) + corps0 #self.transport.write(datagram_UserList,host_port) sendList = [] for u in self.serverProxy.getUserList(): print(u.userChatRoom) if (u.userChatRoom == ROOM_IDS.MAIN_ROOM): sendList.append(u.userAddress) print('send list check', sendList) if (len(sendList) > 0): for i in sendList: self.transport.write(datagram_UserList, i) print('boss int quit 3 test: datagram of user list', datagram_UserList) elif (typeDatagram == 64): print('there is a user speaking') datagram = struct.pack('b', 65) + datagram[1:] lenUsername = datagram[4] userName = struct.unpack('%ds' % lenUsername, datagram[5:5 + lenUsername])[0] userName = userName.decode('utf-8') print('test userbnal talking', userName, len(userName)) uSend = self.serverProxy.getUserByName(userName) print('user name in 64', userName) sendList = [] for u in self.serverProxy.getUserList(): if (not u == uSend and u.userChatRoom == uSend.userChatRoom): sendList.append(u.userAddress) print('test send list', sendList) if (len(sendList) > 0): for i in sendList: self.transport.write(datagram, i)
class c2wUdpChatServerProtocol(DatagramProtocol): def __init__(self, serverProxy, lossPr): """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr self.num_seq = {} self.user_adress = {} self.received_packet_history = {} self.sent_packet_history = {} self.expected_num_seq = {} self.errorList = [ "nonExpectedNumSeq", "wrongUserName", "serverSaturated", "userExists" ] self.timer = {} self.movieRoom = {} self.errorDict = {} for movie in self.serverProxy.getMovieList(): self.serverProxy.startStreamingMovie(movie.movieTitle) self.sendAndWait = True self.queuepacket = [] def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ packet = Packet(0, 0, 0, 0) packet.unpackHeader(datagram) print("SERVER RECEIVING PACKET", packet) if self.serverProxy.getUserByAddress(host_port) is not None: user_name = self.serverProxy.getUserByAddress(host_port).userName elif host_port in self.num_seq.keys(): user_name = host_port else: user_name = None print('host_port', host_port) #if the datagram received is not an ack then we send an ack if packet.message_type != 80: self.ack(packet, host_port) if packet.message_type == 0: self.connectionResponse(datagram, packet, host_port) #if it's and ack of connecion done we send user list then movielist elif (packet.message_type == 80): if packet.num_seq == self.num_seq[user_name]: self.num_seq[user_name] = (self.num_seq[user_name] + 1) % 1023 if self.timer[user_name] != None: self.timer[user_name].cancel() self.timer[user_name] = None self.sendAndWait = True self.sendqueuepacket() if self.lastPacketSent(user_name).message_type == 1: self.updateUserList(ROOM_IDS.MAIN_ROOM) elif self.nlastPacketSent(user_name, 2).message_type == 1: self.sendMovieList(user_name, host_port) elif self.lastPacketSent(user_name).message_type == 2: self.error(self.errorDict[user_name], host_port, user_name) else: return 0 #old ack elif packet.num_seq in self.received_packet_history.keys( ) and self.received_packet_history[packet.num_seq].isEqual(packet): print("paquet ignoré", packet, self.received_packet_history[packet.num_seq]) elif self.expected_num_seq[user_name] != packet.num_seq: self.wrongExpectedNumSeq(host_port, user_name) print("ERROR") #if it's and ack of connecion done we send movie list then userlist #here we are sure that the num sq is verified and that i's not an ack else: self.received_packet_history[user_name][packet.num_seq] = packet self.expected_num_seq[user_name] = ( self.expected_num_seq[user_name] + 1) % 1023 if packet.message_type == 64: print("MESSAGE BIEN RECU") self.forward(packet, datagram, host_port, user_name) elif packet.message_type == 48: self.connectionMovieRoom(datagram, packet, host_port, user_name) elif packet.message_type == 49: self.deconnectionMovieRoom(datagram, packet, host_port, user_name) elif packet.message_type == 3: self.leaveSystem(user_name) elif packet.message == 128: self.errorReceived(datagram, user_name) def forward(self, packet, datagram, host_port, user_name): len_sender = struct.unpack_from('!B', datagram, offset=4)[0] format = '!LB' + str(len_sender) + 'sB' + str(packet.packet_length - 2 - len_sender) + 's' packet.data = [0, 0, 0, 0] headerDec, packet.data[0], packet.data[1], packet.data[2], packet.data[ 3] = struct.unpack(format, datagram) if self.malformed(packet.data[3].decode('utf8')): self.error("malformed message", host_port, user_name=user_name) else: user_chat_room_dict = self.getUserChatRoomDict() if self.movieRoom[packet.data[1].decode( 'utf8')] == ROOM_IDS.MAIN_ROOM: sender_chat_room = "main_room" else: sender_chat_room = self.movieRoom[packet.data[1].decode( 'utf8')] i = 0 while i < len(user_chat_room_dict[sender_chat_room]): receiver = user_chat_room_dict[sender_chat_room][i] print("FORWARD5") messagePacket = Packet(65, self.num_seq[receiver], packet.packet_length, packet.data, format) #messagePacket.packet_format="!LB" + str(self.data[0]) + 's' + "B" + str(self.data[2]) + 's' self.sendPacket( messagePacket, self.serverProxy.getUserByName(receiver).userAddress) print("FORWARD6") i += 1 def connectionMovieRoom(self, datagram, packet, host_port, user_name): packet = Packet(0, 0, 0, 0) packet.unpackHeader(datagram) format = '!LB' + str(packet.packet_length - 1) + 's' packet.data = [0, 0] headerDec, packet.data[0], packet.data[1] = struct.unpack( format, datagram) # the joined room if self.existMovie(packet.data[1].decode("utf-8")) == False: self.error('Movie Room Does Not Exist', host_port, user_name) print(packet.data[1].decode("utf-8"), 'Movie Room Does Not Exist', self.serverProxy.getMovieList()) else: print('the room does exist ') self.movieRoom[user_name] = (packet.data[1].decode("utf-8")) # mettre à jour la liste des utilisateur du movie room dans le serveur self.serverProxy.updateUserChatroom(user_name, self.movieRoom[user_name]) # self.serverProxy.startStreamingMovie(self.movieRoom[user_name]) self.updateUserList(movie_room=self.movieRoom[user_name]) def existMovie(self, movieRoom): Exist = False for movie in self.serverProxy.getMovieList(): if movieRoom == movie.movieTitle: Exist = True return (Exist) def leaveSystem(self, user_name): old_movie_room = self.movieRoom[user_name] self.serverProxy.removeUser(user_name) self.updateUserList(movie_room=old_movie_room) def deconnectionMovieRoom(self, datagram, packet, host_port, user_name): old_movie_room = self.movieRoom[user_name] self.movieRoom[user_name] = ROOM_IDS.MAIN_ROOM self.serverProxy.updateUserChatroom(user_name, self.movieRoom[user_name]) self.updateUserList(movie_room=old_movie_room) print('sent_packet_history', self.sent_packet_history) print('received_packet_history', self.received_packet_history) def connectionResponse(self, datagram, packet, host_port): packet = Packet(0, 0, 0, 0) packet.unpackHeader(datagram) format = '!LB' + str(packet.packet_length - 1) + 's' packet.data = [0, 0] headerDec, packet.data[0], packet.data[1] = struct.unpack( format, datagram) userName = packet.data[1].decode("utf-8") if self.serverProxy.userExists(userName): #num seq a traiter #todo self.expected_num_seq[host_port] = 1 self.num_seq[host_port] = 0 self.sent_packet_history[host_port] = {} self.received_packet_history[host_port] = {} self.failed(host_port) self.errorDict[host_port] = "userExists" elif self.malformed(userName, user_name=True) == True: self.expected_num_seq[host_port] = 1 self.num_seq[host_port] = 0 self.sent_packet_history[host_port] = {} self.received_packet_history[host_port] = {} self.failed(host_port) self.errorDict[host_port] = "malformed User Name" elif len(self.serverProxy.getUserList()) >= 513: self.expected_num_seq[host_port] = 1 self.num_seq[host_port] = 0 self.sent_packet_history[host_port] = {} self.received_packet_history[host_port] = {} self.failed(host_port) self.errorDict[host_port] = "serverSaturated" else: self.expected_num_seq[userName] = 1 self.num_seq[userName] = 0 self.sent_packet_history[userName] = {} self.received_packet_history[userName] = {} self.serverProxy.addUser(userName, ROOM_IDS.MAIN_ROOM, None, host_port) self.movieRoom[userName] = ROOM_IDS.MAIN_ROOM connectionDone = Packet(1, self.num_seq[userName], 0, '') self.sendPacket(connectionDone, host_port) #????? self.serverProxy.updateUserChatroom(userName, ROOM_IDS.MAIN_ROOM) def malformed(self, message, user_name=False): if not user_name: message = message[:-1] return False elif len(message) == 0 or ( len(message) <= 255 and message[0].isalpha() and (len(message) == 1 or message[1:].isalnum())): return False return True def ack(self, packet, host_port): ack = Packet(80, packet.num_seq, 0, None) self.transport.write(ack.pack(), host_port) print('ACKACKACK') def error(self, errorType, host_port, user_name): error = Packet(128, self.num_seq[user_name], len(errorType) + 1, [len(errorType), errorType]) self.sendPacket(error, host_port) def errorReceived(self, datagram, user_name): packet = Packet(0, 0, 0, 0) packet.unpackHeader(datagram) format = '!LB' + str(packet.packet_length - 1) + 's' packet.data = [0, 0] headerDec, packet.data[0], packet.data[1] = struct.unpack( format, datagram) if 'wrongNumSeq:' in packet.data[1].decode('utf8'): self.num_seq[user_name] = int(packet.data[1][12:].decode("utf8")) print("error :", packet.data[1].decode("utf8")) return 0 def wrongExpectedNumSeq(self, host_port, user_name): self.error('wrongNumSeq:' + str(self.expected_num_seq[user_name]), host_port) def failed(self, host_port): failed = Packet(2, self.num_seq[host_port], 0, '') self.sendPacket(failed, host_port) def sendMovieList(self, user_name, host_port): # we send list of movie sorted movie_list = self.serverProxy.getMovieList() movie_list = sorted(movie_list, key=lambda x: x.movieTitle) data = [] length = 2 format = "!LH" data.append(len(movie_list)) for movie in movie_list: format += "B" + str(len(movie.movieTitle)) + "sBBBBH" data.append(len(movie.movieTitle)) data.append(movie.movieTitle.encode("utf-8")) print('movie.movieIpAddress', movie.movieIpAddress) data = data + list(map(int, movie.movieIpAddress.split("."))) data.append(movie.moviePort) # 1 byte -> lenghtmovie title, 4 bytes -> length ipv4 , 2 bytes -> port length = length + 1 + len(movie.movieTitle) + 4 + 2 movie_list_packet = Packet(16, self.num_seq[user_name], length, data, format) self.sendPacket(movie_list_packet, host_port) def updateUserList(self, movie_room): # if user joined a main room: WE SEND TO EACH USER IN MAIN ROOM (EVEN THE USER HIMSELF) "UserListMainRoom" # if user joined a movie room: WE SEND TO EACH USER IN THAT MOVIE ROOM "UserListMovieRoom": # # WE SEND TO EACH USER IN THE MAIN ROOM "UserListMainRoom" user_list = self.serverProxy.getUserList( ) # list of all users connected to the server #update user list in the case of joining the system if movie_room == ROOM_IDS.MAIN_ROOM: #I look for users in that main room and I send them the appropriate list for user in user_list: if user.userChatRoom == ROOM_IDS.MAIN_ROOM: self.sendUserListMainRoom(user.userName, user.userAddress) #update user list else: for user in user_list: if user.userChatRoom == movie_room: self.sendUserListMovieRoom(user, user.userChatRoom) elif user.userChatRoom == ROOM_IDS.MAIN_ROOM: self.sendUserListMainRoom(user.userName, user.userAddress) def sendUserListMovieRoom(self, user, movie_room): #send the Packet of usersList in a movie room to the user user_chat_room_dict = self.getUserChatRoomDict() update_user_list_packet = Packet(18, self.num_seq[user.userName], 0, []) update_user_list_packet.data.append( len(user_chat_room_dict[movie_room])) update_user_list_packet.packet_format = "!LH" update_user_list_packet.packet_length = 2 for user_chat_room in user_chat_room_dict[movie_room]: update_user_list_packet.packet_format += "B" + str( len(user_chat_room)) + "s" update_user_list_packet.packet_length += 1 + len(user_chat_room) update_user_list_packet.data.append(len(user_chat_room)) update_user_list_packet.data.append(user_chat_room.encode("utf8")) self.sendPacket(update_user_list_packet, user.userAddress) def sendUserListMainRoom(self, user_name, host_port): user_list_packet = Packet(19, self.num_seq[user_name], 2, [], "!LH") user_chat_room_dict = {} user_chat_room_dict["main_room"] = [] for movie in self.serverProxy.getMovieList(): user_chat_room_dict[movie.movieTitle] = [] #user_chat_room_dict={"main_room":[5,alice,3,bob];"batmann":[5,mario]... user_list = self.serverProxy.getUserList() for user in user_list: if user.userChatRoom == ROOM_IDS.MAIN_ROOM: user_chat_room_dict["main_room"].append(len(user.userName)) user_chat_room_dict["main_room"].append( user.userName.encode('utf8')) else: user_chat_room_dict[user.userChatRoom].append( len(user.userName)) user_chat_room_dict[user.userChatRoom].append( user.userName.encode('utf8')) user_list_packet.packet_length += 1 + len(user.userName) data = [] #data= [Nombre de room,NombreUtilisateurMainRoom,5,alice,3,bob] user_list_packet.packet_format += "H" data.append(len(user_chat_room_dict.keys())) data.append(int(len(user_chat_room_dict["main_room"]) / 2)) data += user_chat_room_dict["main_room"] user_list_packet.packet_length += 2 i = 0 while i < len(user_chat_room_dict["main_room"]): user_list_packet.packet_format += "B" + str( user_chat_room_dict["main_room"][i]) + "s" i += 2 del user_chat_room_dict["main_room"] for movie in sorted(user_chat_room_dict.keys()): user_list_packet.packet_length += 2 user_list_packet.packet_format += "H" data.append(int(len(user_chat_room_dict[movie]) / 2)) data += user_chat_room_dict[movie] i = 0 while i < len(user_chat_room_dict[movie]): user_list_packet.packet_format += "B" + str( user_chat_room_dict[movie][i]) + "s" i += 2 user_list_packet.data = data self.sendPacket(user_list_packet, host_port) def sendPacket(self, packet, host_port, call_count=1): if self.serverProxy.getUserByAddress(host_port) is not None: user_name = self.serverProxy.getUserByAddress(host_port).userName elif host_port in self.num_seq.keys(): user_name = host_port else: user_name = None if self.sendAndWait == True and call_count == 1: print('SERVER SEND PACKET', packet) self.transport.write(packet.pack(), host_port) self.sent_packet_history[user_name][packet.num_seq] = packet self.sendAndWait = False call_count += 1 if call_count <= 4: self.timer[user_name] = reactor.callLater( 5, self.sendPacket, packet, host_port, call_count) elif call_count > 1: print('SERVER SEND PACKET', packet) self.transport.write(packet.pack(), host_port) call_count += 1 if call_count <= 4: self.timer[user_name] = reactor.callLater( 5, self.sendPacket, packet, host_port, call_count) else: self.queuepacket.append((packet, host_port)) def sendqueuepacket(self): print("SEND AND WAIIIIIT") for packethost in self.queuepacket: self.sendPacket(*packethost) self.queuepacket = [] # UTILS def lastPacketReceived(self, user_name): return self.received_packet_history[user_name][ len(self.received_packet_history[user_name].keys()) - 1] def nlastPacketReceived(self, user_name, i): return self.received_packet_history[user_name][ len(self.received_packet_history[user_name].keys()) - i] def lastPacketSent(self, user_name): print(self.sent_packet_history[user_name].keys(), user_name) return self.sent_packet_history[user_name][ len(self.sent_packet_history[user_name].keys()) - 1] def nlastPacketSent(self, user_name, i): if len(self.sent_packet_history[user_name]) > ( len(self.sent_packet_history[user_name].keys()) - i) and ( len(self.sent_packet_history[user_name].keys()) - i) >= 0: return self.sent_packet_history[user_name][ len(self.sent_packet_history[user_name].keys()) - i] else: return Packet(None, None, None, None) def getUserChatRoomDict(self): """ this method return a dict with dict[movie_room]= [list of user of the movie room] :return: """ user_chat_room_dict = {} user_chat_room_dict["main_room"] = [] for movie in self.serverProxy.getMovieList(): user_chat_room_dict[movie.movieTitle] = [] user_list = self.serverProxy.getUserList() for user in user_list: if user.userChatRoom == ROOM_IDS.MAIN_ROOM: user_chat_room_dict["main_room"].append(user.userName) else: user_chat_room_dict[user.userChatRoom].append(user.userName) return user_chat_room_dict
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address (or the name) of the c2w server. .. attribute:: serverPort The port number used by the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ self.serverAddress = serverAddress self.serverPort = serverPort self.clientProxy = clientProxy self.lossPr = lossPr self.client = clientClass.client(self.clientProxy, self, (self.serverAddress,self.serverPort)) def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendMessage(self, message): self.transport.write(message.raw,(self.serverAddress,self.serverPort)) def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The controller calls this function as soon as the user clicks on the login button. """ self.client.sendLoginRequest(userName) moduleLogger.debug('loginRequest called with username=%s', userName) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called **by the controller** when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ self.client.sendChatMessageOIE(message) def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called **by the controller** when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ self.client.sendJoinRoomRequest(roomName) print "-------------------launching timer------------------------------" moduleLogger.debug('RoomRequest called with roomName=%s', roomName) def sendLeaveSystemRequestOIE(self): """ Called **by the controller** when the user has clicked on the leave button in the main room. """ self.client.sendLeaveRoomMessage() def datagramReceived(self, datagram, (host, port)): """ :param string datagram: the payload of the UDP packet. :param host: the IP address of the source. :param port: the source port. Called **by Twisted** when the client has received a UDP packet. """ print "-------------------Receiving message---------------------------" self.client.testingFragmentation(datagram)
def startProtocol(self): self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ Class implementing the UDP version of the client protocol. Parameters ---------- serverAddress : The IP address (or the name) of the c2w server, given by the user. serverPort : The port number used by the c2w server, given by the user. clientProxy : The clientProxy, which the protocol must use to interact with the Graphical User Interface. Attributes ---------- serverAddress : The IP address of the c2w server. serverPort : The port number of the c2w server. clientProxy : The clientProxy, which the protocol must use to interact with the Graphical User Interface. lossPr : The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.seqNumSent = 0 self.sessionToken = 0 self.timer = 0 self.userID = 0 self.roomRequested = [] self.roomStructure = {} def startProtocol(self): self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendMsg(self, msg, host_port): print(msg) self.transport.write(msg, host_port) def sendLoginRequestOIE(self, userName): """ The client proxy calls this function when the user clicks on the login button. Parameters ---------- userName: string The user name that the user has typed. """ moduleLogger.debug('loginRequest called with username=%s', userName) #connection request version = 0b1 << 28 typ1 = 0b1 << 24 self.sessionToken = 0 hybrid = version + typ1 + self.sessionToken payloadS = len(userName.encode('utf-8')) + 4 USER = userName.encode('utf-8') header = struct.pack('!IHH', hybrid, self.seqNumSent, payloadS) payload = struct.pack('!HH' + str(len(USER)) + 's', self.userID, len(USER), USER) packet = header + payload self.sendMsg(packet, (self.serverAddress, self.serverPort)) self.timer = reactor.callLater(1.0, self.sendMsg, packet, (self.serverAddress, self.serverPort)) self.seqNumSent += 1 def roomStateRequest(self): #request for rooms version = 0b1 << 28 typ = 0b11 << 24 self.type_sent = 3 hybrid = version + typ + self.sessionToken payloadS = 0 header = struct.pack('!IHH', hybrid, self.seqNumSent, payloadS) packet = header self.sendMsg(packet, (self.serverAddress, self.serverPort)) self.timer = reactor.callLater(1.0, self.sendMsg, packet, (self.serverAddress, self.serverPort)) self.seqNumSent += 1 def sendChatMessageOIE(self, message): """ Called by the client proxy when the user has decided to send a chat message Parameters ---------- message : string The text of the chat message. Note ---- This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ self.message_sent.append(message) version = 0b1 << 28 typ = 0b110 << 24 self.type_sent = 6 hybrid = version + typ + self.sessionToken payloadS = len(message) + 2 header = struct.pack('!IHH', hybrid, self.seqNumSent, payloadS) MSG = message.encode('utf-8') payload = struct.pack('!HH' + str(len(MSG)) + 's', self.userID, len(MSG), MSG) packet = header + payload self.sendMsg(packet, (self.serverAddress, self.serverPort)) self.timer = reactor.callLater(1.0, self.sendMsg, packet, (self.serverAddress, self.serverPort)) self.seqNumSent += 1 def sendJoinRoomRequestOIE(self, roomName): """ Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. Parameters ---------- roomName: string The room name (or movie title.) Note ----- The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ self.roomRequested.append(roomName) version = 0b1 << 28 typ = 0b101 << 24 self.type_sent = 5 hybrid = version + typ + self.sessionToken payloadS = 2 idRoom = self.roomStructures[roomName] header = struct.pack('!IHH', hybrid, self.seqNumSent, payloadS) payload = struct.pack('H', idRoom) packet = header + payload self.sendMsg(packet, (self.serverAddress, self.serverPort)) self.timer = reactor.callLater(1.0, self.sendMsg, packet, (self.serverAddress, self.serverPort)) self.seqNumSent += 1 def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ version = 0b1 << 28 typ = 0b111 << 24 self.type_sent = 7 hybrid = version + typ + self.sessionToken payloadS = 0 header = struct.pack('!IHH', hybrid, self.seqNumSent, payloadS) packet = header self.sendMsg(packet, (self.serverAddress, self.serverPort)) self.timer = reactor.callLater(1.0, self.sendMsg, packet, (self.serverAddress, self.serverPort)) self.seqNumSent += 1 def datagramReceived(self, datagram, host_port): """ Called by Twisted when the client has received a UDP packet. Parameters ---------- datagram : string the payload of the UDP packet. host_port: tuple a tuple containing the source IP address and port. """ hybrid, seqNum, payloadS = struct.unpack_from('!IHH', datagram, 0) datagram_typ = (hybrid & 0b1111 << 24) >> 24 datagram_sessionToken = hybrid & 0b111111111111111111111111 self.sessionToken = datagram_sessionToken if datagram_typ == 0: if seqNum == self.seqNumSent - 1: self.timer.cancel() elif datagram_typ == 2: self.acquittement(seqNum, host_port) def acquittement(self, seqNum, host_port): version = 0b1 << 28 typ = 0b0 << 24 hybrid = version + typ + self.sessionToken packet = struct.pack('!IHH', hybrid, seqNum, 0) self.sendMsg(packet, host_port)
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.HEADER_LENGTH = 4 def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ packet_length = self.HEADER_LENGTH + len(userName) seq_num = 0 #always begins connection with a 0 packet_type = 1 seq_num_and_packet_type = seq_num + packet_type encoded_userName = userName.encode('ascii', 'replace') packet_bin = struct.pack("hh" + str(len(userName)) + "s", packet_length, seq_num_and_packet_type, encoded_userName) self.transport.write(packet_bin, (self.serverAddress, self.serverPort)) moduleLogger.debug('loginRequest called with username=%s', userName) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ (packet_length, seq_num_and_type) = struct.unpack_from("hh", datagram, offset=0) packet_type = seq_num_and_type % 16 #We take the last 4 bits seq_num = int(seq_num_and_type / 16) #We cut the last 4 bits print(packet_length) print(packet_type) print(seq_num) # (self.msg,) = struct.unpack_from(str(self.packet_length-self.HEADER_LENGTH)+"s", datagram, offset=self.HEADER_LENGTH) # (timestamp, package_length) = struct.unpack_from("hh", datagram, offset=0) # (msg,) = struct.unpack_from(str(package_length-self.header_length)+"s", datagram, offset=self.header_length) # response = msg.decode('ascii') # self.clientProxy.responseReceived(response) pass
class c2wUdpChatServerProtocol(DatagramProtocol): def __init__(self, serverProxy, lossPr): """ Class implementing the UDP version of the client protocol. Parameters ---------- serverProxy : The serverProxy the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. lossPr : The packet loss probability for outgoing packets. Do not modify this value! """ self.serverProxy = serverProxy self.lossPr = lossPr self.timer = 0 self.roomTitleID = {} self.user_room = {} self.sessionTokens = {} self.sessionTokens[0] = 0 self.seqNumSent = {} self.seqNumSent[0] = 0 def startProtocol(self): self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ hybrid, seqNum, payloadS = struct.unpack_from('!IHH', datagram, 0) datagram_typ = (hybrid & 0b1111 << 24) >> 24 datagram_sessionToken = hybrid & 0b111111111111111111111111 if not (self.serverProxy.getUserByAddress(host_port)): ID = 0 else: if self.serverProxy.getUserByAddress(host_port).userId == None: ID = 0 else: ID = self.serverProxy.getUserByAddress(host_port).userId if datagram_typ == 0: if seqNum == self.seqNumSent[ID] - 1: self.timer.cancel() reactor.run() if seqNum == 0: if datagram_sessionToken != 0: self.roomState(ID, host_port) elif datagram_typ == 1: self.acquittement(seqNum, ID, host_port) self.loginResponse(datagram, host_port) def sendMsg(self, msg, host_port): print(msg) self.transport.write(msg, host_port) def loginResponse(self, datagram, host_port): version = 0b1 << 28 typ = 0b10 << 24 seqNum = 0 payloadS_datagram = struct.unpack_from('!IHH', datagram, 0)[2] l = payloadS_datagram - 4 uid, userNameLen, userNameBin = struct.unpack_from( '!HH' + str(l) + 's', datagram, 8) userName = userNameBin.decode('utf-8') print("username :"******"Main Room" seToken = random.getrandbits(24) self.sessionTokens[ID] = seToken self.seqNumSent[ID] = 0 self.sessionTokens[seToken] = ID hybrid1 = version + typ + self.sessionTokens[ID] payloadS1 = l + 5 else: sessionToken = 0 response = 3 hybrid1 = version + typ + sessionToken ID = 0 payloadS1 = l + 12 header = struct.pack('!IHH', hybrid1, seqNum, payloadS1) payload = struct.pack( '!BHH' + str(len(userName.encode('utf-8'))) + 's', response, ID, len(userName.encode('utf-8')), userName.encode('utf-8')) packet = header + payload self.sendMsg(packet, host_port) self.timer = reactor.callLater(1.0, self.sendMsg, packet, host_port) reactor.run() self.seqNumSent[ID] += 1 def roomState(self, ID, host_port): """ Send the current state of the room """ version = 0b1 << 28 typ = 0b100 << 24 sessionToken = self.sessionTokens[ID] hybrid = version + typ + sessionToken payloadS = 0 userName = self.serverProxy.getUserById(ID).userName #we recover the position of the user in the dict roomName = self.user_room[userName] #list of all the users userListAll = self.serverProxy.getUserList() #if the user is in the main room if roomName == "Main Room": roomId = 1 movieIp = 0 moviePort = 0 payloadS += 8 payloadS += (len(roomName.encode('utf-8')) + 2) payload = struct.pack( '!HH' + str(len(roomName.encode('utf-8'))) + 'sIH', roomId, len(roomName.encode('utf-8')), roomName.encode('utf-8'), movieIp, moviePort) userList = [] for u in userListAll: print("mec al :", u.userName) print("userchatRoom", u.userChatRoom) if self.user_room[u.userName] == roomName: userList.append(u) print("tout user :"******"userRoom = ", self.user_room) print("userList :", userList) if len(userList) == 0: payloadS += 2 payload += struct.pack('!H', 0) else: payloadS += 2 payload += struct.pack('!H', len(userList)) for u in userList: payloadS += 2 payload += struct.pack('!H', u.userId) l = len(u.userName.encode('utf-8')) payloadS += l + 2 payload += struct.pack('!H' + str(l) + 's', l, u.userName.encode('utf-8')) roomList = self.serverProxy.getMovieList() lenght_r = len(roomList) payloadS += 2 payload += struct.pack('!H', lenght_r) for room in roomList: payloadS += 2 payload += struct.pack('!H', room.movieId) print('Movie id: ', room.movieId) l = len(room.movieTitle.encode('utf-8')) payloadS += l + 2 payload += struct.pack('!H' + str(l) + 's', l, room.movieTitle.encode('utf-8')) payloadS += 4 IP = room.movieIpAddress ip = int(ipaddress.ip_address(IP)) print("IP :", ip) payload += struct.pack('!I', ip) payloadS += 2 payload += struct.pack('!H', room.moviePort) userList = [] for u in userListAll: if u.userChatRoom == roomName: userList.append(u) if len(userList) == 0: payloadS += 2 payload += struct.pack('!H', len(userList)) else: for u in userList: payloadS += 2 payload += struct.pack('!H', u.userID) l = len(userList.userName.encode('utf-8')) payloadS += l + 2 payload += struct.pack('!H' + str(l) + 's', l, u.userName.encode('utf-8')) payloadS += 2 payload += struct.pack('!H', 0) else: roomList = self.serverProxy.getMovieList() lenght_r = len(roomList) payloadS += 2 payload += struct.pack('!H', lenght_r) for room in roomList: payloadS += 2 payload += struct.pack('!H', room.movieID) l = len(room.movieTitle.encode('utf-8')) payloadS += l + 2 payload += struct.pack('!H' + str(l) + 's', l, room.movieTitle.encode('utf-8')) payloadS += 4 IP = room.movieIpAddress ip = int(ipaddress.ip_address(IP)) payload += struct.pack('!I', ip) payloadS += 2 payload += struct.pack('!H', room.moviePort) userList = [] for u in userListAll: if u.userChatRoom == roomName: userList.append(u) if len(userList) == 0: payloadS += 2 payload += struct.pack('!H', len(userList)) else: for u in userList: payloadS += 2 payload += struct.pack('!H', u.userID) l = len(userList.userName.encode('utf-8')) payloadS += l + 2 payload += struct.pack('!H' + str(l) + 's', l, u.userName.encode('utf-8')) payloadS += 2 payload += struct.pack('!H', 0) header = struct.pack('!IHH', hybrid, self.seqNumSent[ID], payloadS) packet = header + payload self.sendMsg(packet, host_port) self.timer = reactor.callLater(1.0, self.sendMsg, packet, host_port) self.seqNumSent[ID] += 1 def acquittement(self, seqNum, ID, host_port): version = 0b1 << 28 typ = 0b0 << 24 sessionToken = self.sessionTokens[ID] hybrid = version + typ + sessionToken packet = struct.pack('!IHH', hybrid, seqNum, 0) self.sendMsg(packet, host_port)
class c2wUdpChatServerProtocol(DatagramProtocol): def __init__(self, serverProxy, lossPr): """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ self.managecount={} self.managerepeat = {} self.usserName="" self.manageackul={} self.manageuserseq={} self.rejseq=0 #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport #verify username with the existing data def verifyName(self, userName): if self.serverProxy.userExists(userName): status=0 else: status=1 return status #function to send the login acceptance msg. def loginAcpMsg(self, host_port): sequ_type=(self.manageuserseq[host_port]<<4) | (types["Acceptation connexion"]) buf=struct.pack('!HH',4,sequ_type) self.transport.write(buf,host_port) #retransmit the login acceptance msg if lost. if(self.managecount[host_port]<7): self.managerepeat[host_port] = reactor.callLater(1,self.loginAcpMsg,host_port) self.managecount[host_port]+=1 #send login Refuse msg if username already exist. def sendLoginRefuse(self, host_port): sequ_type=(self.rejseq<<4) | (types["Refus connexion"]) buf=struct.pack('!HH',4,sequ_type) self.transport.write(buf,host_port) #retransmit the login Refuse msg if lost. if(self.managecount[host_port]<7): self.managerepeat[host_port] = reactor.callLater(1,self.sendLoginRefuse,host_port) self.managecount[host_port]+=1 #function to send movies list def sendMovieList(self, host_port): length = 4 for f in self.serverProxy.getMovieList(): length += 9 + len(f.movieTitle.encode('utf-8')) buf=bytearray(length) offset=4 sequ_type=(self.manageuserseq[host_port]<<4) | (types["Envoi liste films"]) struct.pack_into('!HH',buf,0,length,sequ_type) for f in self.serverProxy.getMovieList(): aip=f.movieIpAddress port=f.moviePort iid=f.movieId title=f.movieTitle cip=int(ipaddress.IPv4Address(aip)) lenmovie=9+len(title) struct.pack_into('!IHHB%is'%len(title.encode('utf-8')),buf,offset,cip,port,lenmovie,iid,title.encode('utf-8')) offset += 9+len(title.encode('utf-8')) self.transport.write(buf,host_port) #retransmit the movies list msg if lost. if(self.managecount[host_port]<7): self.managerepeat[host_port] = reactor.callLater(1,self.sendMovieList,host_port) self.managecount[host_port]+=1 #function to send users list def sendUserList(self, host_port): length = 4 for f in self.serverProxy.getUserList(): length += 2 + len(f.userName.encode('utf-8')) buf=bytearray(length) offset=4 sequ_type=(self.manageuserseq[host_port]<<4) | (types["Envoi liste users"]) struct.pack_into('!HH',buf,0,length,sequ_type) for f in self.serverProxy.getUserList(): use=f.userName us=f.userChatRoom print(use,us) if (us==ROOM_IDS.MAIN_ROOM): userstatus=0 else: print(us) userstatus=self.serverProxy.getMovieByTitle(f.userChatRoom).movieId ip_port=f.userAddress lenuser=len(use) print(use,userstatus,ip_port) struct.pack_into('!BB%is'%len(use.encode('utf-8')),buf,offset,lenuser,userstatus,use.encode('utf-8')) offset += 2+len(use.encode('utf-8')) self.transport.write(buf,host_port) #retransmit the users list msg if lost. if(self.managecount[host_port]<7): self.managerepeat[host_port] = reactor.callLater(1,self.sendUserList,host_port) self.managecount[host_port]+=1 #condition to send users list def sendUserListAll(self): print(self.serverProxy.getUserList()) for f in self.serverProxy.getUserList(): self.sendUserList(f.userAddress) #function to forward the chat msg def sendChatMsg(self,username,msg,host_port): lenuser=len(username.encode('utf-8')) length=4+1+lenuser+len(msg.encode('utf-8')) buf=bytearray(length) offset=4 sequ_type=(self.manageuserseq[host_port]<<4) | (types["Message Chat"]) struct.pack_into('!HH',buf,0,length,sequ_type) struct.pack_into('!B%is'%len(username.encode('utf-8')),buf,offset,lenuser,username.encode('utf-8')) offset+=1+lenuser struct.pack_into('!%is'%len(msg.encode('utf-8')),buf,offset,msg.encode('utf-8')) self.transport.write(buf,host_port) if(self.managecount[host_port]<7): self.managerepeat[host_port] = reactor.callLater(1,self.sendChatMsg,username,msg,host_port) self.managecount[host_port]+=1 #condition to send chat msg def sendChatMsgAll(self,username,msg): user=self.serverProxy.getUserByName(username) print(self.serverProxy.getUserList()) for f in self.serverProxy.getUserList(): if f.userChatRoom == user.userChatRoom: self.sendChatMsg(username,msg,f.userAddress) def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ #unpacking and extracting the length, sequence number, type and data from the received msg. lenght, seq_type, data = struct.unpack('!HH%is'%(len(datagram)-4), datagram) seql=seq_type>>4 typ=seq_type & 15 #if type of the received msg is not an ACK, then it will send an ACK for the received msg. if(typ!=types["Acquittement"]): sequ_type=(seql<<4) | (types["Acquittement"]) buf=struct.pack('!HH',4,sequ_type) self.transport.write(buf,host_port) #if Ack is received it will cancel the retransmission of the msg. elif (typ==types["Acquittement"]): self.managerepeat[host_port].cancel() self.manageuserseq[host_port] += 1 self.managecount[host_port] = 0 #once received the ack for acceptance msg with its seql no, add the user to the server and send the movie list if(seql==0): self.serverProxy.addUser(self.usserName,ROOM_IDS.MAIN_ROOM,userChatInstance=None,userAddress=host_port) self.sendMovieList(host_port) elif(seql==1): self.sendUserList(host_port) elif(seql==2): self.sendUserListAll() if(typ==types["Envoi du Pseudo"]): self.usserName=data.decode('utf-8') stat = self.verifyName(self.usserName) if(stat==1): self.manageuserseq[host_port]=0 self.managecount[host_port]=0 self.managerepeat[host_port]=None self.loginAcpMsg(host_port) else: self.sendLoginRefuse(host_port) if(typ==types["Choix d’un film"]): roomName=data.decode('utf-8') self.serverProxy.updateUserChatroom((self.serverProxy.getUserByAddress(host_port)).userName, roomName) self.serverProxy.startStreamingMovie(roomName) self.sendUserListAll() if(typ==types["Quitter salle film"]): self.serverProxy.updateUserChatroom((self.serverProxy.getUserByAddress(host_port)).userName, ROOM_IDS.MAIN_ROOM) self.sendUserListAll() if(typ==types["Quitter application"]): self.serverProxy.removeUser(self.serverProxy.getUserByAddress(host_port).userName) self.sendUserListAll() if(typ==types["Message Chat"]): lenuser=struct.unpack_from('!B',datagram,4)[0] username=struct.unpack_from('!%is'%lenuser,datagram,5)[0].decode('utf-8') lengthmsg=lenght-4-1-lenuser msg=struct.unpack_from('!%is'%lengthmsg,datagram,5+lenuser)[0].decode('utf-8') self.sendChatMsgAll(username,msg) pass
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.c2wClientModel = c2wClientModel() self.lossPr = lossPr self.last_event_id =0 self.seq = 0 self.userID = 0 self.roomID = 0 self.dstRoomID = 0 self.init = False self.messageType = 0x00 self.movieList = [] self.userList = [] self.logged = False self.responses = [] self.lastDGTreated = None self.logged = False def incrementSeq(self) : if (self.seq > 65535) : self.seq = 0 else : self.seq += 1 def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport #--------this function verifies every 60s if the user is connected. If he's not, the application is closed-------------- def verifyConnexion(self): if(self.connected == False): self.clientProxy.applicationQuit() else: self.connected = False reactor.callLater(60, self.verifyConnexion) #GET_PING send a message to the server asking for the last event of the room where the user is def getPing(self, initial): self.messageType = 0x4 msgLength = 4 buf = bytearray(10) last_event_id_2fb = (self.last_event_id & int('111111111111111100000000',2)) >> 8 #retrieve two first bytes in event_id last_event_id_lb = (self.last_event_id) & 255 #retrieve last byte in event_id struct.pack_into('!BHBHHBB', buf, 0, self.messageType, self.seq, self.userID, msgLength, last_event_id_2fb, last_event_id_lb, self.roomID) if self.logged == True : self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, buf, seq, msgType) # if we don't receive response after 500ms, the GET_PING request is resent if initial == True: reactor.callLater(1, self.getPing, True)# this method is called every second, True means it's not a message retry, so it can call a new get ping after a second else: pass else : pass #split in groups of 254 the number of events to be asked to the server def getEvents(self, numberOfEvents): nbIterations = math.ceil(numberOfEvents/254) #we calculate the number of times it will be necessary to ask for events eg: 300/254 = 1.18 -> 2 times while(nbIterations != 0): if(numberOfEvents/254 >= 1): nbDemande = 254 numberOfEvents -= 254 else: nbDemande = numberOfEvents self.events(nbDemande) nbIterations -= 1 #pack and send to the server the GET_EVENTS request def events(self, nbDemande) : self.messageType = 0x6 msgLength = 5 buf = bytearray(11) last_event_id_2fb = (self.last_event_id & int('111111111111111100000000',2)) >> 8 last_event_id_lb = (self.last_event_id) & 255 struct.pack_into('!BHBHHBBB', buf, 0, self.messageType, self.seq, self.userID, msgLength, last_event_id_2fb, last_event_id_lb, nbDemande, self.roomID) self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, nbDemande, seq, msgType) #after a half second, we call a function that checks if the response was received #PUT_LOGIN send a message to the server to inform it of the user's wish to enter the server def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) self.messageType = 0x00 usernameLength = len(userName.encode('utf-8')) #len returns the number of characters msgLength = usernameLength + 1 # 1 for UL field buf = bytearray(7+usernameLength) #2 bytes for seq, 1 for userID,... struct.pack_into('!BHBHB'+str(usernameLength)+'s', buf, 0, self.messageType, self.seq, self.userID, msgLength, usernameLength, userName.encode('utf-8')) self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, userName, seq, msgType) #after a half second, we call a function that checks if the response was received #PUT_NEW_MESSAGE send a message to the server with the text the user has typed in the chatroom wether it is in the MAIN_ROOM or a MOVIE_ROOM def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ self.messageType = 0x0E msgLength = 1 + 2 + len(message) #1 byte for room_id, 2 for text length... buf = bytearray(9+len(message)) #2 bytes for seq, 1 pour userID,... struct.pack_into('!BHBHBH'+str(len(message))+'s', buf, 0, self.messageType, self.seq, self.userID, msgLength, self.roomID, len(message), message.encode('utf-8')) #the result is saved in buf self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, message, seq, msgType) #after a half second, we call a function that checks if the response was received #PUT_SWITCH_ROOM send a message to the server to inform it of the user's wish to change rooms def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ if (roomName == ROOM_IDS.MAIN_ROOM) : self.dstRoomID = 0 else : room = self.c2wClientModel.getMovieByTitle(roomName) self.dstRoomID = room.movieId self.messageType = 0x0C msgLength = 1 #1 for ROOM_ID buf = bytearray(7) struct.pack_into('!BHBHB', buf, 0, self.messageType, self.seq, self.userID, msgLength, self.dstRoomID) self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, roomName, seq, msgType) #after a half second, we call a function that checks if the response was received #PUT_LOGOUT send a message to the server to inform it of the user's wish to leave the server def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ self.messageType = 0x02 msgLength = 0 buf = bytearray(6) struct.pack_into('!BHBH', buf, 0, self.messageType, self.seq, self.userID, msgLength) self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, buf, seq, msgType) #after a half second, we call a function that checks if the response was received # GET_ROOMS send a message to the server to ask for the list of movies def getRooms(self) : self.messageType = 0x08 msgLength = 2 #1 for FIRST_ROOM_ID 1 for NBR_ROOMS buf = bytearray(8) struct.pack_into('!BHBHBB', buf, 0, self.messageType, self.seq, self.userID, msgLength, 1, 255) self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, buf, seq, msgType) #after a half second, we call a function that checks if the response was received #GET_USERS send a message to the user to ask for the list of users in the server (MAIN_ROOM) or in the room (MOVIE_ROOM) def getUsers(self, roomID) : self.messageType = 0x0A msgLength = 3 buf = bytearray(9) struct.pack_into('!BHBHBBB', buf, 0, self.messageType, self.seq, self.userID, msgLength, 1, 255, self.roomID) self.transport.write(buf, (self.serverAddress, self.serverPort)) seq = self.seq msgType = self.messageType self.incrementSeq() reactor.callLater(0.5, self.verifyResponse, roomID, seq, msgType) #after a half second, we call a function that checks if the response was received #unpack the RESPONSE_USERS datagram and return the list of users received (userName, userRoom) def unpackUsersList(self, datagram) : usersPack = struct.unpack('!BHBHB'+str(len(datagram)-7)+'s', datagram) nbUsers = usersPack[4] users = usersPack[5] #USERS list (variable length) listUsers = users userTuples=[] while (nbUsers != 0) : user1 = struct.unpack('!BB'+str(len(listUsers)-2)+'s', listUsers) #separate USER_ID, UL and the rest user11 = struct.unpack('!'+str(user1[1])+'sB'+str(len(user1[2])-user1[1]-1)+'s', user1[2]) #separate USERNAME, ROOM_ID and the rest userID = user1[0] userName = user11[0].decode('utf-8') roomID = user11[1] if (roomID == 0) : userRoom = ROOM_IDS.MAIN_ROOM movieName = userRoom else : userRoom = ROOM_IDS.MOVIE_ROOM if self.init == True : #for loggin initialisation, we just need to know if the user is in the main room or in the movie room movieName = userRoom else : movie = self.c2wClientModel.getMovieById(roomID) movieName = movie.movieTitle userTuples.append((userName, userRoom)) #add USERNAME and ROOM_ID to the list of pair user=self.c2wClientModel.getUserByName(userName) if user != None : pass else : self.c2wClientModel.addUser(userName, userID, movieName) #store user informations nbUsers-=1 listUsers = user11[2] #make the initial packet equal to the rest, in order to retrieve the other users through further iterations self.c2wClientModel.updateUserChatroom(userName, userRoom) if (self.init == False) : self.clientProxy.userUpdateReceivedONE(userName, movieName) #won't be execute for the initial response user else : pass return userTuples #unpack the RESPONSE_ROOMS datagram and return the list of movies received (movieTitle, movieIP, moviePort) def unpackRoomsList(self, datagram) : moviesPack = struct.unpack('!BHBHB'+str(len(datagram)-7)+'s', datagram) nbMovies = moviesPack[4] movies = moviesPack[5] listMovies = movies moviesTriplets=[] while (nbMovies != 0) : movie1 = struct.unpack('!B4BHB'+str(len(listMovies)-8)+'s', listMovies) #separate ROOM_ID, IP, PORT_NUMBER, RNL and the rest movieID = movie1[0] moviePort = movie1[5] movieIP = str(movie1[1])+"."+str(movie1[2])+"."+str(movie1[3])+"."+str(movie1[4]) movie11 = struct.unpack('!'+str(movie1[6])+'sB'+str(len(movie1[7])-movie1[6]-1)+'s', movie1[7]) #separate ROOM_NAME, NBR_USERS and the rest movieTitle = movie11[0].decode('utf-8') moviesTriplets.append((movieTitle, movieIP, moviePort)) #add ROOM_NAME, IP and PORT_NUMBER to the list of triplets self.c2wClientModel.addMovie(movieTitle, movieIP, moviePort, movieID) #store movie informations nbMovies-=1 listMovies = movie11[2] #make the initial packet equal to the rest, in order to retrieve the other videos through further iterations print (moviesTriplets) return moviesTriplets #unpack the RESPONSE_EVENTS datagram and execute the necessary updates def unpackEvents(self, datagram) : eventsPack = struct.unpack('!BHBHB'+str(len(datagram)-7)+'s', datagram) #separate MESSAGE_TYPE, SEQ_NUMBER, USER_ID, MESSAGE_LENGTH (header), NBR_EVENTS and the events nbEvents = eventsPack[4] events = eventsPack[5] while(nbEvents != 0) : event1 = struct.unpack('!HBBBB'+str(len(events)-6)+'s', events) #separate EVENT_ID, EVENT_TYPE, ROOM_ID, USER_ID, and the rest self.last_event_id = (event1[0]<<8)|event1[1] roomID = event1[3] userID = event1[4] eventType = event1[2] #MESSAGE event: MESSAGE_LENGTH, MESSAGE if (eventType==0x1) : event11 = struct.unpack('!H'+str(len(event1[5])-2)+'s', event1[5]) #separate MESSAGE_LENGTH (chat) and the rest event111 = struct.unpack('!'+str(event11[0])+'s'+str(len(event11[1])-event11[0])+'s', event11[1]) #separate MESSAGE and the rest message = event111[0].decode('utf-8') user = self.c2wClientModel.getUserById(userID) #retrieve the user using USER_ID userName = user.userName if userID != self.userID and self.roomID == roomID : #print the msg only if the message is from another user in the same room. (care of duplication) self.clientProxy.chatMessageReceivedONE(userName, message) else : pass events = event111[1] #NEW_USER event: USERNAME_LENGTH, USERNAME elif (eventType==0x2) : event11 = struct.unpack('!B'+str(len(event1[5])-1)+'s', event1[5]) #separate UL and the rest event111 = struct.unpack('!'+str(event11[0])+'s'+str(len(event11[1])-event11[0])+'s', event11[1]) #separate USERNAME and the rest if (roomID == 0) : userRoom = ROOM_IDS.MAIN_ROOM movieName= userRoom else : userRoom = ROOM_IDS.MOVIE_ROOM movie = self.c2wClientModel.getMovieById(roomID) movieName = movie.movieTitle userName = event111[0].decode('utf-8') user=self.c2wClientModel.getUserByName(userName) if user != None : #prevent to add user if he exists, because we add user after response user. pass else : self.c2wClientModel.addUser(userName, userID, movieName) #store user informations self.c2wClientModel.updateUserChatroom(userName, userRoom) self.clientProxy.userUpdateReceivedONE(userName, movieName) events = event111[1] #SWITCH_ROOM event: NEW_ROOM_ID elif (eventType==0x3) : event11 = struct.unpack('!B'+str(len(event1[5])-1)+'s', event1[5]) #separate NEW_ROOM_ID and the rest newRoomID = event11[0] if (newRoomID == 0) : room = ROOM_IDS.MAIN_ROOM movieName = room else : room = ROOM_IDS.MOVIE_ROOM movie = self.c2wClientModel.getMovieById(newRoomID) movieName = movie.movieTitle user = self.c2wClientModel.getUserById(userID) #retrieve the user using USER_ID userName = user.userName self.c2wClientModel.updateUserChatroom(userName, room) self.clientProxy.userUpdateReceivedONE(userName, movieName) events = event11[1] #LOGOUT event: elif (eventType==0x4) : user = self.c2wClientModel.getUserById(userID) #retrieve the user using USER_ID userName = user.userName self.clientProxy.userUpdateReceivedONE(userName, ROOM_IDS.OUT_OF_THE_SYSTEM_ROOM) self.c2wClientModel.removeUser(userName) events = event1[5] else : pass nbEvents-=1 def leaveApplication(self): self.clientProxy.connectionRejectedONE("Connexion lost") reactor.callLater(2, self.clientProxy.applicationQuit) self.logged = False #identify the response message type and execute the necessary actions def treatDatagram(self, datagram) : received = struct.unpack('!BH'+str(len(datagram)-3)+'s', datagram) msgType = received[0] #RESPONSE_LOGIN if (msgType == 0x01) : received = struct.unpack('!BHBHBBHB', datagram) #unpack the received data status_code = received[4] #successful login if (status_code==0x00) : self.init = True self.logged = True self.last_event_id = (received[6]<<8)|received[7] #shift the 2 first bytes of the last_event_id to the left and apply a bitwise OR with the last byte self.userID = received[5] self.getUsers(self.roomID) self.connectedTimer = reactor.callLater(60, self.leaveApplication) #if no data received every 60s, we conclude that the connexion is lost # connectedTimer is reinitialised after each data reception #unsuccessful login elif (status_code==0x01) : self.clientProxy.connectionRejectedONE("unknown error") elif (status_code==0x02) : self.clientProxy.connectionRejectedONE("too many users") elif (status_code==0x03) : self.clientProxy.connectionRejectedONE("invalid username") elif (status_code==0x04) : self.clientProxy.connectionRejectedONE("username not available") else : self.clientProxy.connectionRejectedONE("error") #RESPONSE_LOGOUT elif (msgType == 0x03) : received = struct.unpack('!BHBHB', datagram) #unpack the received data status_code = received[4] if (status_code == 0) : self.clientProxy.leaveSystemOKONE() self.logged = False self.connectedTimer.cancel() #stop timmer else : pass #RESPONSE_PING get the difference betwen server's and user's last event id's elif (msgType == 0x05) : received = struct.unpack('!BHBHHB', datagram) #unpack the received data containing last event id last_event_id = (received[4]<<8)|received[5] if (last_event_id>self.last_event_id): #if server last_event_id is greater than the client diff = last_event_id - self.last_event_id self.getEvents(diff) #client asks for the remaining events elif (last_event_id<self.last_event_id): #if client last_event_id is greater than the server's, it means the server has reached the max seq and then reinitialised it diff = last_event_id + (math.pow(2,24) - self.last_event_id) #(math.pow(2,24) - self.last_event_id) : events that took place in the server before reinitialisation self.getEvents(diff) #client asks for the events before and after seq reinitialisation else: pass #RESPONSE_EVENTS elif (msgType == 0x07) : self.unpackEvents(datagram) #RESPONSE_USERS elif (msgType == 0x0B) : self.userList = self.unpackUsersList(datagram) #get the list of users in the list of pairs format (USERNAME, ROOM_ID) if self.init == True : #self.init set to True when successful loggin and to false when we got response rooms. we call getRooms after response user only for loggin self.getRooms() else : pass #RESPONSE_ROOMS elif (msgType == 0x09) : self.movieList = self.unpackRoomsList(datagram) #get the list of triplets with movies' info self.clientProxy.initCompleteONE(self.userList, self.movieList) #when init is true, it means we have just logged in, and so initialisation is complete reactor.callLater(1, self.getPing, True) #call getPing 1s after complete login self.init = False #RESPONSE_SWITCH_ROOM elif (msgType == 0x0D) : received = struct.unpack('!BHBHB', datagram) #unpack the received data status_code = received[4] if (status_code==0x0) : self.roomID = self.dstRoomID self.getUsers(self.roomID) user = self.c2wClientModel.getUserById(self.userID) userName = user.userName if (self.roomID == 0) : userRoom = ROOM_IDS.MAIN_ROOM movieName = userRoom else : userRoom = ROOM_IDS.MOVIE_ROOM movie = self.c2wClientModel.getMovieById(self.roomID) movieName = movie.movieTitle self.c2wClientModel.updateUserChatroom(userName, userRoom) self.clientProxy.userUpdateReceivedONE(userName, movieName) self.clientProxy.joinRoomOKONE() else : pass #RESPONSE_NEW_MESSAGE elif (msgType == 0x0F) : received = struct.unpack('!BHBHB', datagram) #unpack the received data status_code = received[4] if (status_code==0x0) : pass else : pass else: pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ if self.logged == True : self.connectedTimer.reset(60) else : pass received = struct.unpack('!BH'+str(len(datagram)-3)+'s', datagram) msgType = received[0] seq = received[1] #the case in which we receive the login message (it's always the first message, so we can execute it whitout checking if there are others with an inferior sequence number) if self.lastDGTreated == None and seq == 0 : self.treatDatagram(datagram) self.lastDGTreated = 0 #the case in which the message is the expected, we can execute it without worries, and after that we check the list of responses received for ensuing messages elif seq == self.lastDGTreated + 1 : self.treatDatagram(datagram) self.lastDGTreated = self.lastDGTreated + 1 quit = False while quit == False : i = 0 quit = True for item in self.responses : if (item[1] == self.lastDGTreated + 1): self.treatDatagram(item[2]) self.lastDGTreated = self.lastDGTreated + 1 del self.responses[i] #remove the treated data in self.responses quit = False break else : i += 1 #the case in which there's a gap in the sequence number, it means that one or more packets weren't received. We store the datagram in a list elif seq > self.lastDGTreated + 1 : self.responses.append((msgType, seq, datagram)) else : pass #check if the response was received, called in general 500ms after sending the message def verifyResponse(self, parameter, respSeq, messageType): #respSeq is used to be sure that the received response is the expected #the case in which the lastDGTreated is still uninitialized, that means the login response wasn't received. We then resend the request if respSeq == 0 and self.lastDGTreated == None : self.seq -= 1 self.sendLoginRequestOIE(parameter) #the case in which the lastDGTreated already surpassed the seq number we are checking. It means the datagram was already received and executed, so we don't have to do anything elif respSeq <= self.lastDGTreated : return #the case in which the lastDGTreated is inferior to the seq number we are checking. It means the datagram wasn't executed yet, so we have to search it in the list elif respSeq > self.lastDGTreated : i = 0 for item in self.responses : #the case in which the response datagram was already received, but wasn't executed, we have to wait for the messages with an inferior seq number to be executed if (item[1] == respSeq): return else : i += 1 #the case in which the response datagram wasn't received yet (it's not in the list), we have to resend the request tempSeq = self.seq self.seq = respSeq #if we didn't receive the expected seq response, we resend the request with the expected seq (respSeq) print(self.seq) if(messageType == 0x02): self.sendLeaveSystemRequestOIE() elif(messageType == 0x04): self.getPing(False) #false parameter means it is a retry, so we don't call a new get ping after a second elif(messageType == 0x06): self.getEvents(parameter) elif(messageType == 0x08): self.getRooms() elif(messageType == 0x0A): self.getUsers(self.roomID) elif(messageType == 0x0C): self.sendJoinRoomRequestOIE(parameter) elif(messageType == 0x0E): self.sendChatMessageOIE(parameter) else: pass self.seq = tempSeq #continue the code with our current seq else : pass
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.filattente=[] self.host_port= (self.serverAddress,self.serverPort) self.listeFilms=[] #la liste des films envoyée par le serveur self.listeUsers=[] #la liste des utilisateurs envoyée par le serveur(Main et Movies Rooms) self.listeUsersMovie=[] #la liste des utilisateurs qui sont dans les movie room self.monNumeroSequence=0 # numero de sequence du client qui sera incrémenté au fur et à mesure self.room= " " #il s'agit de la room dans laquelle se trouve l utilisateur à un instant t self.numeroSequenceWhenGoingMovie=0 # le numero de sequence lorsque l'utilisateur fait la demande pour une movie self.numeroSequenceWhenGoingMain=0 # le numero de sequence lorsque l'utilisateur fait la demande pour revenir dans la main self.numeroSequenceWhenOutMain=0 # le numero de sequence lorsque l'utilisateur fait la demande de deconnexion self.OnlyTitleAndIdOfMovie=[] # liste contenant les films et leur ID self.nomUtilisateur="" #contient le username de l'utilisateur self.connectivite=0 #Fonction qui envoie un acuittement au serveur def envoieAcquittement(self,numeroSequence): TypeAcq = 0 decalage= numeroSequence << 4 seqTypAcq= decalage | TypeAcq buff= struct.pack('!hh',4,seqTypAcq) self.transport.write(buff,(self.serverAddress,self.serverPort)) #Fonction qui permet d'incrementer le numero de sequence jusqu'à 4095 def incrementerNumeroSequence(self,numSequence): if (numSequence==4095): #4095= (2 exposant 12)-1 numSequence=0 else: numSequence+=1 return numSequence # fonction pour verifier si on a recu un ack def traiterAcquittement(self,numSeq): for p in self.filattente: if (p[0]==numSeq): p[2]=1 print(p) print(self.filattente) print("Acquittement bien recu") #fonction pour envoyer le paquet si jamais on a toujours pas recu d ack def send_And_Wait(self,hostPort): for j in self.filattente: if (j[4]==hostPort): if (j[1] <= 7): # 7 correspond au nombre maximum de fois qu'on doit ramener un paquet if (j[2] == 0): self.transport.write(j[3],hostPort) j[1]+=1 reactor.callLater(1,self.send_And_Wait,hostPort) elif(j[2] == 1): print("Confirmation acquittement bien recu") self.filattente.remove(j) else: print("le paquet a djaaaaaaa") self.filattente.remove(j) self.clientProxy.applicationQuit() #Fonction pour dépaqueter la liste des films envoyée par le serveur def paquetListFilms(self,monpaquet): while(len(monpaquet)>0): #longueurMonPaquet=len(monpaquet) longueurFilm=int((struct.unpack('!h',monpaquet[6:8]))[0]) print("longueur de film est :",longueurFilm," et de type :", type(longueurFilm)) longeurTitreFilm=longueurFilm-9 portFilm= int((struct.unpack('!h',monpaquet[4:6]))[0]) adresseIpAConvertir=(struct.unpack('!I',monpaquet[0:4]))[0] adresseIp= str(ipaddress.IPv4Address(adresseIpAConvertir)) IdMovie=int((struct.unpack('!b',monpaquet[8:9]))[0]) titreFilm=(struct.unpack('!%is'%longeurTitreFilm,monpaquet[9:longueurFilm]))[0].decode('utf-8') print("film: ", titreFilm) print("port :",portFilm) print("adresse IP : ",adresseIp,"de type", type(adresseIp)) self.listeFilms.append((titreFilm,adresseIp,portFilm)) self.OnlyTitleAndIdOfMovie.append([titreFilm,IdMovie]) #print("la liste des films recu est",self.listeFilms) monpaquet=monpaquet[longueurFilm:] print("la liste des films est:", self.listeFilms) #Fonction pour dépaqueter la liste des utilisateurs envoyée par le serveur def paquetListUser(self,monpaquet): self.listeUsers=[] self.listeUsersMovie=[] while(len(monpaquet)>0): longueurUserName=int((struct.unpack('!b',monpaquet[0:1]))[0]) print("longueur USERNAME est :",longueurUserName," et de type :", type(longueurUserName)) #print("longueur USERNAME est :%i et de type : %s"%(longueurUserName, type(longueurUserName))) #longeurTitreFilm=longueurFilm-9 statut= int((struct.unpack('!b',monpaquet[1:2]))[0]) print("statut :", statut) nomUtilisateur= (struct.unpack('!%is'%longueurUserName,monpaquet[2:(longueurUserName+2)]))[0].decode('utf-8') print("USER: "******"on a deux listes qui sont:",self.listeUsers,"\n",self.listeUsersMovie) monpaquet=monpaquet[(longueurUserName+2):] #Fonction permettant de former le paquet pour rejoindre une movie particulière def paquetForParticularMovie(self,numSequence,movieName): print(movieName) a=4+len(movieName) print(a) Type = 3 #decalage= NumSeq << 4 decalage= numSequence << 4 seqTyp= decalage | Type print(seqTyp) buf= struct.pack('!hh%is'%len(movieName),a,seqTyp,movieName.encode('utf-8')) print("le paquet pour la movie room est :",buf) return buf #Fonction permettant de former le paquet pour retourner dans la main room def retourMainRoom(self,numSequence): Type = 4 decalage= numSequence << 4 seqTyp= decalage | Type print(seqTyp) buf= struct.pack('!hh',4,seqTyp) print("le paquet pour la main room est :",buf) return buf #Fonction pour depaqueter un message de chat recu def msgChatRecu(self,monpaquet): longueurPseudo=int((struct.unpack('!b',monpaquet[0:1]))[0]) pseudo=(struct.unpack('!%is'%longueurPseudo,monpaquet[1:(longueurPseudo+1)]))[0].decode('utf-8') sms=(struct.unpack('!%is'%(len(monpaquet)-longueurPseudo-1),monpaquet[(longueurPseudo+1):]))[0].decode('utf-8') print("emetteur :",pseudo, "message :", sms) return (pseudo,sms) #Fonction permettant de former le paquet pour quitter la main room def quitterMainRoom(self,numSequence): Type = 2 decalage= numSequence << 4 seqTyp= decalage | Type print(seqTyp) buf= struct.pack('!hh',4,seqTyp) print("le paquet pour la main room est :",buf) return buf def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) #self.transport.connect(self.serverAddress,self.serverPort) #connection au serveur self.nomUtilisateur=userName print(userName) a=4+len(userName) print(a) #NumSeq=0 Type = 1 decalage= self.monNumeroSequence << 4 seqTyp= decalage | Type print(seqTyp) buf= struct.pack('!hh%is'%len(userName),a,seqTyp,userName.encode('utf-8')) print(buf) self.transport.write(buf,(self.serverAddress,self.serverPort)) #self.filattente.append([0,1,0,buf,server]) self.filattente.append([self.monNumeroSequence,1,0,buf,(self.serverAddress,self.serverPort)]) self.monNumeroSequence=self.incrementerNumeroSequence(self.monNumeroSequence) reactor.callLater(1,self.send_And_Wait,(self.serverAddress,self.serverPort)) pass def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ a=4+1+len(self.nomUtilisateur)+len(message) Type = 9 decalage= self.monNumeroSequence << 4 seqTyp= decalage | Type print(seqTyp) buf= struct.pack('!hhb'+str(len(self.nomUtilisateur))+'s%is'%len(message),a,seqTyp,len(self.nomUtilisateur),self.nomUtilisateur.encode('utf-8'),message.encode('utf-8')) print("message de chat",buf) self.transport.write(buf,(self.serverAddress,self.serverPort)) self.filattente.append([self.monNumeroSequence,1,0,buf,(self.serverAddress,self.serverPort)]) self.monNumeroSequence=self.incrementerNumeroSequence(self.monNumeroSequence) reactor.callLater(1,self.send_And_Wait,(self.serverAddress,self.serverPort)) pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ #pour aller dans une movie room if (self.room== "MainRoom"): buf=self.paquetForParticularMovie(self.monNumeroSequence,roomName) self.numeroSequenceWhenGoingMovie= self.monNumeroSequence print("J'ai fait un paquet pour acceder à une movie room") self.transport.write(buf,(self.serverAddress,self.serverPort)) self.filattente.append([self.monNumeroSequence,1,0,buf,(self.serverAddress,self.serverPort)]) self.monNumeroSequence=self.incrementerNumeroSequence(self.monNumeroSequence) reactor.callLater(1,self.send_And_Wait,(self.serverAddress,self.serverPort)) self.room="MovieRoom" #pour quitter une movie room elif (self.room=="MovieRoom"): print("zizaggggggggggggggggggggggggggggggggggggggggggggggg") buf=self.retourMainRoom(self.monNumeroSequence) self.numeroSequenceWhenGoingMain=self.monNumeroSequence self.transport.write(buf,(self.serverAddress,self.serverPort)) self.filattente.append([self.monNumeroSequence,1,0,buf,(self.serverAddress,self.serverPort)]) self.monNumeroSequence=self.incrementerNumeroSequence(self.monNumeroSequence) reactor.callLater(1,self.send_And_Wait,(self.serverAddress,self.serverPort)) self.room="MainRoom" pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ buf=self.quitterMainRoom(self.monNumeroSequence) self.numeroSequenceWhenOutMain=self.monNumeroSequence self.transport.write(buf,(self.serverAddress,self.serverPort)) self.filattente.append([self.monNumeroSequence,1,0,buf,(self.serverAddress,self.serverPort)]) self.monNumeroSequence=self.incrementerNumeroSequence(self.monNumeroSequence) reactor.callLater(1,self.send_And_Wait,(self.serverAddress,self.serverPort)) pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ reception=struct.unpack('!hh%is'%(len(datagram)-4),datagram) print(reception) seqType= int(str(reception[1])) corpsMessage=reception[2] print("le cors du msg est :",corpsMessage) Type= seqType & 15 NumSeq=seqType >> 4 if (Type==0): self.traiterAcquittement(NumSeq) # après reception d'un acquittement pour rejoindre la movie room, on y va if(NumSeq>0 and self.numeroSequenceWhenGoingMovie==NumSeq): print("l'utilisateur est dans la movie room") self.clientProxy.joinRoomOKONE() self.numeroSequenceWhenGoingMovie=0 #self.room="MovieRoom" #print("******************************************",self.room) # après reception d'un acquittement pour quitter la movie room, on retourne donc la main room if(self.numeroSequenceWhenGoingMain==NumSeq and NumSeq>0): print("on retourne dans la Main Room") self.clientProxy.joinRoomOKONE() self.numeroSequenceWhenGoingMain=0 #self.room="MainRoom" # print("******************************************",self.room) # Acquittement pour demande de départ du système if (self.numeroSequenceWhenOutMain==NumSeq and self.numeroSequenceWhenOutMain!=0 ): self.clientProxy.leaveSystemOKONE() print("BYEBYEBYEBYEBYEBYEBYEBYE") #acquittement de l'acceptation de connexion if (self.connectivite==0 and Type==7): """TypeAcq = 0 decalage= NumSeq << 4 seqTypAcq= decalage | TypeAcq print("sequence et type concaténé pour le 1er acquittement est", seqTypAcq) buff= struct.pack('!hh',4,seqTypAcq) self.transport.write(buff,(host_port[0],host_port[1]))""" self.envoieAcquittement(NumSeq) # envoie de l'acquittement au serveur self.room= "MainRoom" self.connectivite=1 print("l'utilisateur est dans la ",self.room ) #Fin acquittement de l'acceptation de connexion # On recoit la liste des films if(Type==5): self.envoieAcquittement(NumSeq) # envoie de l'acquittement au serveur self.paquetListFilms(corpsMessage) #On recoit la liste des utilisateurs if(Type==6): if(NumSeq==2): self.envoieAcquittement(NumSeq) # envoie de l'acquittement au serveur self.paquetListUser(corpsMessage) print("************************************",self.listeUsers) print("************************************",self.listeFilms) self.clientProxy.initCompleteONE(self.listeUsers,self.listeFilms) #permet d'afficher la Main Room #cela signifie qu'il s'agit d'une mise à jour de la liste des users recue, par le serveur if(NumSeq>2): print("LE PAQUET DE MISE A JOUR DES UITILISATEUS EST ARRIVE") self.envoieAcquittement(NumSeq) self.paquetListUser(corpsMessage) if (self.room=="MainRoom"): print("MISE A JOUR DE LA LISTE DES UTILISATEURS DE LA MAIN ROOM") print(self.listeUsers) self.clientProxy.setUserListONE(self.listeUsers) print("FIN MISE A JOUR DE LA LISTE DES UTILISATEURS DE LA MAIN ROOM") elif (self.room=="MovieRoom"): print("MISE A JOUR DE LA LISTE DES UTILISATEURS DE LA MOVIE ROOM") print(self.listeUsers) self.clientProxy.setUserListONE(self.listeUsersMovie) print("FIN MISE A JOUR DE LA LISTE DES UTILISATEURS DE LA MOVIE ROOM") #refus de connexion if(Type==8): print("Vous n'avez pas été autorisé à vous connecter") self.envoieAcquittement(NumSeq) if(self.connectivite==0): self.clientProxy.connectionRejectedONE("Pseudo trop long ou déja utilisé") self.connectivite=1 #Lorsqu'on recoit un message de chat if(Type==9): self.envoieAcquittement(NumSeq) pseudo,sms=self.msgChatRecu(corpsMessage) if(self.nomUtilisateur!=pseudo): self.clientProxy.chatMessageReceivedONE(pseudo, sms) print("Un nouveau message") pass
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.seq = 1 self.tailleHeader = 4 self.dicoFilm = { } # dictionnaire {Id:titreFilm,.....} ex:{"5":"Big Bunny",....} self.compteurSAW = 0 #Useful for the programming of the send & wait policy self.user = "" # le nom d utilisateur que le client va utiliser: ca ne changera pas self.deco = -1 #pour savoir si le dernier message envoyé est une déconnexion totale du système def incrementeSeq(self): if (self.seq < 1023): self.seq += 1 else: self.seq = 1 def sendAndWait(self, message): """ Takes in parameter : - the binary message (datagram) ready to be sent Sets the SendAndWait functionality : it is called by different send-methods (apart from sendAcknowledgeOIE) to send a non-ack datagram to the server """ if (self.compteurSAW < 10): print("compteur = {0}".format(self.compteurSAW)) print("{0} envoie: {1}".format(self.user, decoder(message))) self.transport.write(message, (self.serverAddress, self.serverPort)) self.compteurSAW += 1 self.RerunFunction = reactor.callLater(1, self.sendAndWait, message) #reactor.run() else: print( "la valeur max du compteur est atteinte, message non transmis") #self.RerunFunction.cancel() self.compteurSAW = 0 def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) dico = {} dico["taille"] = self.tailleHeader + len(userName.encode('utf-8')) dico["Type"] = 1 dico["seq"] = self.seq dico["user"] = userName print(userName) self.user = userName connexionRq = encoder(dico) self.sendAndWait(connexionRq) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ #on prépare le message pour l'envoie au serveur dicoMessage = {} dicoMessage["taille"] = self.tailleHeader + len( message.encode('utf-8')) dicoMessage["Type"] = 5 dicoMessage["seq"] = self.seq dicoMessage["message"] = message msgchat = encoder(dicoMessage) self.sendAndWait(msgchat) pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ #on prépare le message pour l'envoie au serveur dicoRequete = {} dicoRequete["taille"] = self.tailleHeader + 1 dicoRequete["Type"] = 6 dicoRequete["seq"] = self.seq if roomName == ROOM_IDS.MAIN_ROOM: #if the user wants to go to the main room, the room ID is set to 0 dicoRequete["Id"] = 0 else: #we have to find the room ID which corresponds to the given roomName. for Id, nom in self.dicoFilm.items( ): # in order to do this, we can use the attibute "dicoFilm", a dictionnary with (str(roomID), roomName) as items if nom == roomName: dicoRequete["Id"] = int(Id) joindreRq = encoder(dicoRequete) self.sendAndWait(joindreRq) pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ logoutDico = {} logoutDico["taille"] = self.tailleHeader logoutDico["Type"] = 9 logoutDico["seq"] = self.seq paquet = encoder(logoutDico) self.deco = self.seq self.sendAndWait(paquet) pass def sendAcknowledgeOIE( self, ackSeq ): #ackSeq est la sequence contenue dans le message à acquitter """ Send an aknowledgment message """ ackDico = {} ackDico["taille"] = self.tailleHeader ackDico["Type"] = 63 ackDico["seq"] = ackSeq ackDatagram = encoder(ackDico) print("{0} envoie un ack de sequence: {1}".format(self.user, ackSeq)) self.transport.write(ackDatagram, (self.serverAddress, self.serverPort)) def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ messageDico = decoder( datagram ) #The datagram's fields are decoded in a dictionnary (messageDico) print("{0} recoit: {1}".format(self.user, messageDico)) Type = messageDico.get("Type") if Type == 2: #le datagram contient la liste des films self.movieList = [ ] #list of 3-elements-Tuples [(filmName,filmIpAdresse,filmPort)...]. used by the client Proxy (initCompleteONE()) to display the available movies i = 0 while "taille{0}".format( i ) in messageDico: #to fill in the movieList and the dicoFilm dictionnary #print(messageDico) tupleTemporaire = (str(messageDico["nom{}".format(i)]), str(messageDico["ip{}".format(i)]), str(messageDico["port{}".format(i)])) self.movieList.append(tupleTemporaire) self.dicoFilm["{0}".format(messageDico["Id{}".format( i)])] = messageDico["nom{0}".format( i)] #ex: dicoFilm={"5":'Big Buck Bunny',..... } #print(self.dicoFilm) i += 1 ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) if Type == 3: #Le datagram contient la liste des utilisateurs self.userList = [ ] #list of 2-elements-Tuples [(userName,movieTitle)...]. used by the client Proxy (initCompleteONE()) to display the online users i = 0 while "taille{0}".format( i) in messageDico: #to fill in the userList if messageDico["Id{0}".format(i)] == 0: roomName = ROOM_IDS.MAIN_ROOM else: #find the movieTitle which matches the room ID of each client roomId = messageDico["Id{0}".format( i)] #roomId est un str contenant un decimal: ex "5" roomName = self.dicoFilm["{0}".format(roomId)] tupleTemporaire = (str(messageDico["user{0}".format(i)]), roomName) #print("tupletemporaire={0}".format(tupleTemporaire)) self.userList.append(tupleTemporaire) i += 1 #print(self.userList) #print(self.movieList) ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) self.clientProxy.initCompleteONE( self.userList, self.movieList ) #affiche les listes des films disponibles et des utilisateurs connectes if Type == 4: # le datagram est une MAJ utilisateur roomId = messageDico["Id"] userName = messageDico["user"] if roomId == 0: #l'utilisateur veualler dans la salle principale roomName = ROOM_IDS.MAIN_ROOM elif roomId == 255: #deconnexion totale du systeme roomName = ROOM_IDS.OUT_OF_THE_SYSTEM_ROOM else: # l' utilisateur veut aller dans une movieRoom spécifique for Id, nom in self.dicoFilm.items( ): #chercher le titre du Film associé à cet Id (et pas le nom générique ROOM_IDS.MOVIE_ROOM) if int(Id) == roomId: roomName = nom self.clientProxy.userUpdateReceivedONE( userName, roomName ) #MAJ la list des utilisateurs dans le systeme et la modifier sur l'interface graphique ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) if Type == 7: # inscription acceptee ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) if Type == 8: # error: inscription refusee errorCode = messageDico[ "erreur"] # recuperer le code erreur contenu dans le message et informer le client selon la valeur du code if errorCode == 1: self.clientProxy.connectionRejectedONE( "nom d'utilisateur déjà utilisé") elif errorCode == 2: self.clientProxy.connectionRejectedONE( "nom d'utilisateur dépassant 254 octets") elif errorCode == 3: self.clientProxy.connectionRejectedONE( "nom d'utilisateur contenant un ou plusieurs espaces") ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) if Type == 10: #recevoir un message de chat des autres utilisateurs if not messageDico[ "user"] == self.user: #Pour ne pas afficher un message dont le client est l'auteur rUserName = messageDico["user"] message = messageDico["message"] self.clientProxy.chatMessageReceivedONE( rUserName, message ) #afficher le message sur l'interface graphique avec son auteur ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) if Type == 11: #demande de connexion a une salle acceptee par le serveur self.clientProxy.joinRoomOKONE( ) #commander la connection de l'interface graphique à la salle de film que j'ai demandée ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) if Type == 12: #demande de connexion a une salle rejetee par le serveur ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) self.clientProxy.connectionRejectedONE( "Echec: veuillez vous reconnecter" ) #message erreur + reouverture de l interface pour une nouvelle tentative if Type == 63: if messageDico[ "seq"] == self.seq: #le message recu est bien un ack à mon dernier message self.RerunFunction.cancel( ) # arreter le renvoi programmé dans la methode sendAndWait if self.deco == self.seq: # si mon dernier message était une deconnexion totale self.clientProxy.leaveSystemOKONE( ) #fermer l interface graphique #on incrémente le numéro de séquence self.incrementeSeq( ) # N incrementer la sequence que si j'ai recu le bon acquittement self.compteurSAW = 0 #Reinitialise le compteur du sensAndWait return messageDico
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr # numero de sequence du paquet courant self.numeroSeq_courant = 0 # numero de sequence du paquet qu'on a envoyé en dernière position self.numeroSeq_Prec = 0 #on garde dans cette liste tous les paquets envoyés jusqu'a obtention de leur ack self.paquet_memoire = [] #liste des utilisateurs self.users = [] #numero de sequence du paquet envoyé lorsqu'on veut acceder a une movie room self.numeroSeq_GoToMovie = 0 #numero de sequence du paquet envoyé lorsqu'on veut se deconnecter du système self.deconnexion = 0 #numero de l utilisateur self.username = '' #room dans laquelle l utilisateur se trouve self.room = '' #numero de sequence du paquet envoyé lorqu on veut quitter la movie room self.main_rome = 0 #variable pour recuperer le nom du film de l utilisateur lorqu il accede a la movie room self.roomName = '' #-------------------------------------------------------------------------- # Fonction pour gerer les numeros de sequence #-------------------------------------------------------------------------- def NumSeq(self, numSeq): #on genere un numero de sequence pour chaque paquet envoyé #il est initialisé à 0 et incremente de 1 a chaque nouvel envoi #jusqu'a 8191 où il revient à 0 if (numSeq == 4095): numSeq = 0 else: numSeq += 1 return numSeq #-------------------------------------------------------------------------- # Fonction pour definir les formats de paquet #-------------------------------------------------------------------------- def FormatPaquet(self, Type, numSeq, longueur, messageTotal): longueur = len(messageTotal) entete = (Type << 12) + (numSeq << 16) + longueur paquet = struct.pack('!II' + str(len(messageTotal)) + 's', entete, messageTotal.encode('utf−8')) return (paquet) #-------------------------------------------------------------------------- # Fonction pour definir le format de qu paquet de login #-------------------------------------------------------------------------- def PaquetLogin(self, Type, numSeq, userName): self.numeroSeq_courant = numSeq userNamepack = struct.pack( str(len(userName.encode('utf−8'))) + 's', userName.encode('utf−8')) longueur = len(userNamepack) + 4 entete = (Type << 28) + (numSeq << 16) + longueur paquet = struct.pack('!I', entete) + userNamepack return (paquet) #-------------------------------------------------------------------------- # Fonction pour recuperer les inforamtions des paquets recu #-------------------------------------------------------------------------- def PaquetRecu(self, datagram): print(datagram) (entete, messageTotal) = struct.unpack('!I' + str(len(datagram) - 4) + 's', datagram) entete1 = entete >> 28 Type = entete1 & int('1111', 2) entete2 = entete >> 16 numSeq = entete2 & int('0000111111111111', 2) longueur = entete & int('1111111111111111', 2) return (Type, numSeq, longueur, messageTotal) #-------------------------------------------------------------------------- # Fonction pour definir le foramts des paquets d'Ack #-------------------------------------------------------------------------- def FormatAck(self, Type, numSeq): longueur = 4 entete = (Type << 28) + (numSeq << 16) + longueur paquet = struct.pack('!I', entete) return (paquet) #-------------------------------------------------------------------------- # Fonction pour recuperer la liste des utilisateurs de la Main Room #-------------------------------------------------------------------------- def PaquetUser(self, datagram): # on affiche le paquet recu print(" ".join("{:02x}".format(donnee) for donnee in datagram)) # liste des utilisateurs du systeme userList = list() # liste des utilisateurs se trouvant dans la meme movie room que l utilisateur users_movie = list() #on separe l entete du message utile (entete, messageTotal) = struct.unpack('!I' + str(len(datagram) - 4) + 's', datagram) print(entete) entete1 = entete >> 28 Type = entete1 & int('1111', 2) entete2 = entete >> 16 numSeq = entete2 & int('0000111111111111', 2) longueur = entete & int('1111111111111111', 2) datagram = datagram[4:] print('le datagram est:' + str(datagram)) longueurMes = len(messageTotal) #on recupere les informations recu jusqu a ce qu il n y a plus rien dans le paquet while (longueurMes != 0): longName = struct.unpack('!B', datagram[0:1])[0] print(longName) datagram = datagram[1:] print('taille du nom :' + str(longName)) user_name = struct.unpack( str(longName) + 's', datagram[0:longName])[0] user_name = user_name.decode('utf-8') datagram = datagram[longName:] statut = struct.unpack('B', datagram[0:1])[0] if (statut == 0): statutUser = ROOM_IDS.MAIN_ROOM else: statutUser = ROOM_IDS.MOVIE_ROOM users_movie.append((user_name, self.roomName)) userList.append((user_name, statutUser)) datagram = datagram[1:] longueurMes = longueurMes - (longName + len(str(statut)) + len(str(longName))) print('Toute la liste est', userList) return (userList, users_movie) #---------------------------------------------------------------- # Fonction pour la liste des videos disponibles #---------------------------------------------------------------- def PaquetMovie(self, datagram): # liste contenant la longueur, le nom, l adresse IP et le port des films movieList = list() #on separe l'entete du message en lui meme (entete, messageTotal) = struct.unpack('!I' + str(len(datagram) - 4) + 's', datagram) entete1 = entete >> 28 Type = entete1 & int('1111', 2) entete2 = entete >> 16 numSeq = entete2 & int('0000111111111111', 2) longueur = entete & int('1111111111111111', 2) datagram = datagram[4:] longueurMes = len(messageTotal) #on recupere les informations recu jusqu a ce qu il n y a plus rien dans le paquet while (longueurMes > 0): longMovie = struct.unpack('!B', datagram[0:1])[0] datagram = datagram[1:] movie_name = struct.unpack( str(longMovie) + 's', datagram[0:longMovie])[0] movie_name = movie_name.decode('utf-8') datagram = datagram[longMovie:] # on recupere l adresse IP et le port de chaque film adr_ip = struct.unpack('!I', datagram[0:4])[0] adr_ip3 = adr_ip & 255 adr_ip = adr_ip >> 8 adr_ip2 = adr_ip & 255 adr_ip = adr_ip >> 8 adr_ip1 = adr_ip & 255 adr_ip = adr_ip >> 8 adr_ip0 = adr_ip ip_adress = str.join( ".", (str(adr_ip0), str(adr_ip1), str(adr_ip2), str(adr_ip3))) datagram = datagram[4:] port = struct.unpack('!H', datagram[0:2])[0] #on ajoute chaque film dans la movieList movieList.append((movie_name, ip_adress, port)) datagram = datagram[2:] longueurMes = longueurMes - (longMovie + (len(str(ip_adress)) - 3) + len(str(port)) + len(str(longMovie))) print('les films disponibles sont', movieList) return (movieList) #-------------------------------------------------------------------------- # Fonction pour aller dans une movie room #-------------------------------------------------------------------------- def FormatSelectMovie(self, Type, numSeq, movieName): self.numeroSeq_courant = numSeq longueur = len(movieName) + 4 entete = (Type << 28) + (numSeq << 16) + longueur paquet = struct.pack('!I' + str(len(movieName.encode('utf-8'))) + 's', entete, movieName.encode('utf−8')) return (paquet) #-------------------------------------------------------------------------- # Fonction pour quitter la salle de video #-------------------------------------------------------------------------- def FormatLeaveRoom(self, Type, numSeq): self.numeroSeq_courant = numSeq longueur = 4 entete = (Type << 28) + (numSeq << 16) + longueur paquet = struct.pack('!I', entete) return (paquet) #-------------------------------------------------------------------------- # Fonction pour les messages #-------------------------------------------------------------------------- # fonction pour envoyer un message def EnvoiMsg(self, Type, numSeq, username, message): paquetMes = struct.pack( '!B' + str(len(username.encode('utf−8'))) + 's' + str(len(message.encode('utf−8'))) + 's', len(username.encode('utf−8')), username.encode('utf−8'), message.encode('utf−8')) longSpeudo = len(self.username.encode('utf−8')) longMsg = len(message.encode('utf−8')) self.numeroSeq_courant = numSeq longueur = longSpeudo + longMsg + 1 + 4 entete = (Type << 28) + (numSeq << 16) + longueur paquet = struct.pack('!I', entete) + paquetMes print('la paquet est:' + str(paquet)) return (paquet) # fonction pour recuperer les informations lorqu on recoit un message def MsgRecu(self, datagram): (entete, messageTotal) = struct.unpack('!I' + str(len(datagram) - 4) + 's', datagram) print(entete) entete1 = entete >> 28 Type = entete1 & int('1111', 2) entete2 = entete >> 16 numSeq = entete2 & int('0000111111111111', 2) longueur = entete & int('1111111111111111', 2) datagram = datagram[4:] longMes = longueur - 4 longName = struct.unpack('!B', datagram[0:1])[0] datagram = datagram[1:] user_name = struct.unpack(str(longName) + 's', datagram[0:longName])[0] user_name = user_name.decode('utf-8') datagram = datagram[longName:] longMessage = longMes - 1 - longName message = struct.unpack( str(longMessage) + 's', datagram[0:longMessage])[0] message = message.decode('utf-8') return (user_name, message) #---------------------------------------------------------------- # Fonction pour send & wait #---------------------------------------------------------------- # fonction pour verifier si on a recu un ack def traitementAck(self, numSeq): for p in self.paquet_memoire: if (p[0] == numSeq): p[2] = 1 print('ack envoye par le serveur') #fonction pour envoyer le paquet si jamais on a toujours pas recu d ack def sendAndWait(self, host_port): for p in self.paquet_memoire: if (p[4] == host_port): if (p[1] <= 7): if (p[2] == 0): self.transport.write(p[3], host_port) p[1] += 1 print('nombre de message envoye:' + str(p[1])) reactor.callLater(1, self.sendAndWait, host_port) elif (p[2] == 1): print('Le paquet a ete aquitte') self.paquet_memoire.remove(p) else: print('Le paquet envoye est perdu') self.paquet_memoire.remove(p) def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) print(userName) # on forme le paquet pour le requete de connexion paquet = self.PaquetLogin(1, 0, userName) print(paquet) print(" ".join("{:02x}".format(donnee) for donnee in paquet)) #on sauvegarde le nom de l utilisateur lorsqu il fait une requete de connexion self.username = userName # on envoie le paquet au serveur server = (self.serverAddress, self.serverPort) self.transport.write(paquet, server) #on sauvegarde le paquet envoyé self.paquet_memoire.append([0, 1, 0, paquet, server]) self.numeroSeq_Prec = self.numeroSeq_courant self.numeroSeq_courant = self.NumSeq(self.numeroSeq_courant) #on attend 1s, si on n a toujours pas recu d ack on renvoit le paquet au serveur reactor.callLater(1, self.sendAndWait, server) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ paquet = self.EnvoiMsg(7, self.numeroSeq_courant, self.username, message) print(paquet) print(" ".join("{:02x}".format(donnee) for donnee in paquet)) # on envoie le paquet au serveur server = (self.serverAddress, self.serverPort) self.transport.write(paquet, server) self.paquet_memoire.append( [self.numeroSeq_courant, 1, 0, paquet, server]) self.numeroSeq_Prec = self.numeroSeq_courant self.numeroSeq_courant = self.NumSeq(self.numeroSeq_courant) reactor.callLater(1, self.sendAndWait, server) pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ server = (self.serverAddress, self.serverPort) #pour acceder a une movie room if (self.room == 'main_room'): paquet = self.FormatSelectMovie(2, self.numeroSeq_courant, roomName) self.numeroSeq_GoToMovie = self.numeroSeq_courant self.roomName = roomName self.transport.write(paquet, server) self.paquet_memoire.append( [self.numeroSeq_courant, 1, 0, paquet, server]) self.numeroSeq_Prec = self.numeroSeq_courant self.numeroSeq_courant = self.NumSeq(self.numeroSeq_courant) reactor.callLater(1, self.sendAndWait, server) #pour sortir de la movie room elif (self.room == 'movie_room'): paquet = self.FormatLeaveRoom(3, self.numeroSeq_courant) self.main_rome = self.numeroSeq_courant self.transport.write(paquet, server) self.paquet_memoire.append( [self.numeroSeq_courant, 1, 0, paquet, server]) self.numeroSeq_Prec = self.numeroSeq_courant self.numeroSeq_courant = self.NumSeq(self.numeroSeq_courant) reactor.callLater(1, self.sendAndWait, server) pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ server = (self.serverAddress, self.serverPort) paquet = self.FormatLeaveRoom(4, self.numeroSeq_courant) self.deconnexion = self.numeroSeq_courant self.transport.write(paquet, server) self.paquet_memoire.append( [self.numeroSeq_courant, 1, 0, paquet, server]) self.numeroSeq_Prec = self.numeroSeq_courant self.numeroSeq_courant = self.NumSeq(self.numeroSeq_courant) reactor.callLater(1, self.sendAndWait, server) pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ #on recupere les informations du paquet recu print(" ".join("{:02x}".format(donnee) for donnee in datagram)) (Type, numSeq, longueur, messageTotal) = self.PaquetRecu(datagram) #------------------------------------------------------------------------- #on a recu un ack if (Type == 0): self.traitementAck(numSeq) print('ackkk') # on a recu un ack apres une requete pour acceder a la movie room # on rejoint donc cette movie room if (self.numeroSeq_GoToMovie == numSeq and numSeq > 0): print('rejoint la movie room') self.clientProxy.joinRoomOKONE() self.numeroSeq_GoToMovie = 0 self.room = 'movie_room' # on a recu un ack apres un requete pour quitter la movie room #on rejoint donc la main room if (self.main_rome == numSeq and numSeq > 0): print('sortie movie room') self.clientProxy.joinRoomOKONE() self.main_rome = 0 self.room = 'main_room' # on a recu un ack apres une requete pour quitter le système # on sort donc de l application if (self.deconnexion == numSeq): if (self.deconnexion != 0): self.clientProxy.leaveSystemOKONE() print('l utilisateur a été suprimé') #------------------------------------------------------------------------- # on envoie un ack au serveur if (Type != 0): print('message recu') ack = self.FormatAck(0, numSeq) print('ack envoyé au serveur:' + str(ack)) self.transport.write(ack, host_port) print('ack bien envoyé au serveur avec seqnum:' + str(numSeq)) #------------------------------------------------------------------------- # on a recu la liste des utilisateurs if (Type == 6): if (numSeq == 1): (userList, usersMovie) = self.PaquetUser(datagram) self.users = userList print('la liste :' + str(self.users)) elif (numSeq > 1): (userList, usersMovie) = self.PaquetUser(datagram) if (self.room == 'main_room'): self.clientProxy.setUserListONE(userList) elif (self.room == 'movie_room'): self.clientProxy.setUserListONE(usersMovie) #------------------------------------------------------------------------- # on a recu la liste des videos disponibles if (Type == 5): movieList = self.PaquetMovie(datagram) print('la liste des videos est:' + str(movieList)) print('la liste est:' + str(self.users)) self.clientProxy.initCompleteONE(self.users, movieList) self.room = 'main_room' print(movieList) #------------------------------------------------------------------------- # on a recu un message if (Type == 7): (userNom, message) = self.MsgRecu(datagram) self.clientProxy.chatMessageReceivedONE(userNom, message) print('le message qu on a recu est:' + str(message)) #------------------------------------------------------------------------- # on a recu un message d erreur apres notre requete de connexion if (Type == 9): erreur = 'errreur' self.clientProxy.connectionRejectedONE(erreur) pass
class c2wUdpChatClientProtocol(DatagramProtocol): UserIdDict = {} def __init__(self, serverAddress, serverPort, clientProxy, lossPr, lastSequenceNumberSentJR=None, lastSequenceNumberSentLS=None, lastSequenceNumberSentLR=None, lastSequenceNumberSentSM=None, lastSequenceNber=0): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.SequenceNumber = 0 self.UserId = 0 self.SessionToken = 0 self.movieList = [] self.lastSequenceNumberSentJR = lastSequenceNumberSentJR self.lastSequenceNumberSentLS = lastSequenceNumberSentLS self.lastSequenceNumberSentSM = lastSequenceNumberSentSM self.ACKLR = False self.counterLR = 0 self.ACKJR = False self.counterJR = 0 self.counterSM = 0 self.ACKSM = False self.lastSequenceNumberSentLR = lastSequenceNumberSentLR self.lastSequenceNber = lastSequenceNber def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ if self.counterLR == 4 and self.ACKLR == False: self.clientProxy.applicationQuit() Version = 1 Type = 1 self.counterLR += 1 usName = userName.encode('utf-8') uName = struct.pack('>H' + str(len(usName)) + 's', len(usName), usName) Payload = struct.pack('>H' + str(len(uName)) + 's', 0, uName) Psize = len(Payload) self.lastSequenceNumberSentLR = self.SequenceNumber """not important""" LoginRequest = struct.pack( '>BBHHH' + str(Psize) + 's', Version * 2**4 + Type, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), self.SequenceNumber, Psize, Payload) ##if self.SequenceNumber==self.lastSequenceNber: if self.ACKLR == False: self.transport.write(LoginRequest, (self.serverAddress, self.serverPort)) self.lastSequenceNber = self.lastSequenceNumberSentLR if self.ACKLR == False and self.counterLR <= 3: reactor.callLater(1, self.sendLoginRequestOIE, userName) #Version=1 #Type=1 #SessionToken=0 #SequenceNumber=0 #userName=userName.encode('utf-8') #UserName=struct.pack('>H'+str(len(userName))+'s',len(userName),userName) #Payload=struct.pack('>H'+str(len(UserName))+'s',0,UserName) #PayloadSize=len(Payload) #LRQ=struct.pack('>BBHHH'+str(PayloadSize)+'s',Version*2**4+Type,SessionToken//(2**16),SessionToken-(2**16)*SessionToken//(2**16),SequenceNumber,PayloadSize,Payload) #self.transport.write(LRQ,(self.serverAddress,self.serverPort)) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ if self.counterSM == 3 and self.ACKSM == False: self.clientProxy.applicationQuit() Version = 1 Type = 6 self.counterSM += 1 self.lastSequenceNumberSentSM = self.SequenceNumber Message = message.encode('utf-8') Pld = struct.pack('>HH' + str(len(Message)) + 's', self.UserId, len(Message), Message) PldSize = len(Pld) ChatMessage = struct.pack( '>BBHHH' + str(PldSize) + 's', Version * 2**4 + Type, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), self.SequenceNumber, PldSize, Pld) print(self.ACKSM) if self.ACKSM == False: self.transport.write(ChatMessage, (self.serverAddress, self.serverPort)) self.lastSequenceNber = self.SequenceNumber if self.ACKSM == False and self.counterSM <= 3: self.SequenceNumber = self.SequenceNumber - 1 reactor.callLater(1, self.sendChatMessageOIE, message) self.SequenceNumber += 1 if self.counterSM == 1: self.ACKSM = False pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ if roomName == ROOM_IDS.MAIN_ROOM: roomID = 1 else: for i in self.movieList: if i[0] == roomName: roomID = i[3] if self.counterJR == 3: self.clientProxy.applicationQuit() Version = 1 Type = 5 self.counterJR += 1 self.lastSequenceNumberSentJR = self.SequenceNumber GTR = struct.pack( '>BBHHHH', Version * 2**4 + Type, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), self.SequenceNumber, 2, roomID) print(self.ACKJR) if self.ACKJR == False: self.transport.write(GTR, (self.serverAddress, self.serverPort)) self.lastSequenceNber = self.lastSequenceNumberSentJR if self.ACKJR == False and self.counterJR <= 3: self.SequenceNumber = self.SequenceNumber - 1 reactor.callLater(1, self.sendJoinRoomRequestOIE, roomName) self.SequenceNumber += 1 if self.counterJR == 1: self.ACKJR = False pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ Version = 1 Type = 7 self.lastSequenceNumberSentLS = self.SequenceNumber LOR = struct.pack( '>BBHHH', Version * 2**4 + Type, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), self.SequenceNumber, 0) self.transport.write(LOR, (self.serverAddress, self.serverPort)) pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ Packet = struct.unpack('>BBHHH' + str(len(datagram) - 8) + 's', datagram) Version = Packet[0] // (2**4) Type = Packet[0] - (2**4) * (Packet[0] // (2**4)) self.SessionToken = Packet[1] * (2**16) + Packet[2] self.SequenceNumber = Packet[3] PayloadSize = Packet[4] Payload = Packet[5] if Type == 0: if self.SequenceNumber == self.lastSequenceNumberSentLR: self.ACKLR = True if self.SequenceNumber == self.lastSequenceNumberSentJR: self.ACKJR = True self.counterJR = 0 #print('me here' + str(self.SessionToken)) self.clientProxy.joinRoomOKONE() self.RRS() if self.SequenceNumber == self.lastSequenceNumberSentLS: self.clientProxy.leaveSystemOKONE() #self.RRS() if self.SequenceNumber == self.lastSequenceNumberSentSM: self.ACKSM = True self.counterSM = 0 if Type != 0: ## if the datagram isn't an ACK we have to send one to the server Ack = struct.pack( '>BBHHH', 16, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), self.SequenceNumber, 0) self.transport.write(Ack, host_port) if Type == 2: #print(self.SessionToken) self.UserId = struct.unpack('>BH' + str(len(Payload) - 3) + 's', Payload)[1] ResponseCode = struct.unpack('>B' + str(len(Payload) - 1) + 's', Payload)[0] if ResponseCode == 2: self.clientProxy.connectionRejectedONE( 'Connection failed : Username too long!') if ResponseCode == 1: self.clientProxy.connectionRejectedONE( 'Connection failed : invalid Username!') if ResponseCode == 3: self.clientProxy.connectionRejectedONE( 'Connection failed : Username already taken!') if ResponseCode == 4: self.clientProxy.connectionRejectedONE( 'Connection failed : Service not available!') if Type == 4 and self.SequenceNumber == 1: c2wUdpChatClientProtocol.UserIdDict = decodeRSTPayload(Payload)[2] (userList, self.movieList) = decodeRSTPayload(Payload)[0:2] self.clientProxy.initCompleteONE(userList, self.movieList) if Type == 4 and self.SequenceNumber != 1: c2wUdpChatClientProtocol.UserIdDict = decodeRSTPayload(Payload)[2] userList = decodeRSTPayload(Payload)[0] #print(userList) self.clientProxy.setUserListONE(userList) #print(self.SessionToken) if Type == 6: MessageandId = struct.unpack('>HH' + str(len(Payload) - 4) + 's', Payload) Message = MessageandId[2].decode('utf-8') Id = MessageandId[0] username = c2wUdpChatClientProtocol.UserIdDict[Id] print('me' + str(username)) self.clientProxy.chatMessageReceivedONE(username, Message) if Type == 3: userList = decodeRSTPayload(Payload)[0] c2wUdpChatClientProtocol.UserIdDict = decodeRSTPayload(Payload)[2] for user in userList: self.clientProxy.userUpdateReceivedONE(user[0], user[1]) def RRS(self): Version = 1 Type = 3 self.SequenceNumber += 1 #print('RRS :',self.SequenceNumber) RRS = struct.pack( '>BBHHH', Version * 2**4 + Type, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), self.SequenceNumber, 0) self.transport.write(RRS, (self.serverAddress, self.serverPort)) self.lastSequenceNber = self.SequenceNumber pass
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address (or the name) of the c2w server. .. attribute:: serverPort The port number used by the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ self.serverAddress = serverAddress self.serverPort = serverPort self.clientProxy = clientProxy self.lossPr = lossPr self.idcall = None self.status = None self.userID = None self.seq_num = 0 self.expected_seq_num = 0 self.listOfUserName = [] self.listOfUserId = () self.userNames = [] self.listOfTitles = [] self.listOfMovieId = [] self.usernamesUpdated = 0 self.listOfMovies = [] self.NbrMovie = None self.NbrUser = None self.current_movie_room_id = None self.requested_movie_room_id = None self.type_last_message = None self.connected = False self.requestMovieName = None self.MovieName = None def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ if type(userName) == str: moduleLogger.debug('loginRequest called with username=%s', userName) TYPE = '0001' self.type_last_message = int(TYPE, 2) A = '0' R = '0' ACK_NUM = 0 DATA = userName buffer_length = 6 + len(DATA) buf = ctypes.create_string_buffer(buffer_length) fourbyte = (int(TYPE, 2) << 28) + (int(A, 2) << 27) + ( int(R, 2) << 26) + (int(str(ACK_NUM), 2) << 13) + ( int(str(self.seq_num), 2) << 0) struct.pack_into('>IH' + str(len(DATA)) + 's', buf, 0, fourbyte, buffer_length, DATA) self.transport.write(buf.raw, (self.serverAddress, self.serverPort)) self.idcall = reactor.callLater(3.0, self.sendLoginRequestOIE, [userName]) else: if type(userName) == list: if not (userName == []): first_username = userName[0] self.sendLoginRequestOIE(first_username) userName.pop(0) self.sendLoginRequestOIE(userName) else: return def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ ACK_NUM = 0 self.send_Public_Message(ACK_NUM, self.seq_num, self.userID, message) self.idcall = reactor.callLater(3.0, self.sendChatMessageOIE, [message]) def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ ACK_NUM = 0 if (roomName == ROOM_IDS.MAIN_ROOM): self.requested_movie_room_id = 0 self.status = 1 self.MovieName = ROOM_IDS.MAIN_ROOM self.send_MainRoom_Request(ACK_NUM, self.seq_num, self.userID) else: indexOfRoom = self.listOfTitles.index(roomName) self.MovieName = roomName self.status = 0 movieId = self.listOfMovieId[indexOfRoom] self.send_MovieRoom_Request(ACK_NUM, self.seq_num, self.userID, movieId) self.idcall = reactor.callLater(3.0, self.sendJoinRoomRequestOIE, [roomName]) def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ ACK_NUM = 0 self.disconnect(ACK_NUM, self.seq_num, self.userID) self.clientProxy.applicationQuit() self.idcall = reactor.callLater(3.0, self.sendLeaveSystemRequestOIE) def datagramReceived(self, data, (host, port)): """ :param data: The message received from the server :type data: A string of indeterminate length Twisted calls this method whenever new data is received on this connection. """ j = struct.unpack_from('!I', data[0:])[0] if (j == 12): l = data[4:].split('.') for item in l: if not (item == ''): self.listOfMovieId.append(int(item)) else: dataParsed = core.parseReceivedData(data) TYPE = dataParsed[0] data_extracted = dataParsed[6] if (TYPE == 2 and self.connected == False): self.AnalyseUserMessage(data_extracted) self.clientProxy.initCompleteONE(self.listOfUserName, self.listOfMovies) self.sendAck(dataParsed[5]) self.connected = True elif (TYPE == 2 and self.connected == True and self.status == 1): self.AnalyseUserMessage(data_extracted) self.clientProxy.setUserListONE(self.listOfUserName) elif (TYPE == 2 and self.connected == True and self.status == 0): self.AnalyseUserMessageForMovieRoom(self.MovieName, data_extracted) self.clientProxy.setUserListONE(self.listOfUserName) elif (TYPE == 3): self.AnalyseMovieMessage(data_extracted) self.sendAck(dataParsed[5]) elif (TYPE == 4): PublicMessage_infos = core.extract_public_message( data_extracted) userId = PublicMessage_infos[0] indiceUser = self.listOfUserId.index(userId) username = self.listOfUserName[indiceUser][0] self.clientProxy.chatMessageReceivedONE( username, PublicMessage_infos[2]) self.sendAck(dataParsed[5]) elif (TYPE == 5): PrivateMessage_infos = core.extract_private_message( data_extracted) userId = PrivateMessage_infos[0] indiceUser = self.listOfUserId.index(userId) username = self.listOfUserName[indiceUser][0] self.clientProxy.chatMessageReceivedONE( username, PrivateMessage_infos[2]) self.sendAck(dataParsed[5]) elif (TYPE == 14): self.idcall.cancel() self.ReceiveLoginResponse(data_extracted) self.sendAck(dataParsed[5]) elif (TYPE == 15): self.idcall.cancel() self.seq_num += 1 if self.type_last_message == 8: self.clientProxy.leaveSystemOKONE() elif (self.type_last_message == 6): self.current_movie_room_id = self.requested_movie_room_id self.clientProxy.joinRoomOKONE() elif (self.type_last_message == 7): self.current_movie_room_id = self.requested_movie_room_id self.clientProxy.joinRoomOKONE() elif (TYPE == 0): self.extract_error(data_extracted)
class c2wUdpChatServerProtocol(DatagramProtocol): def __init__(self, serverProxy, lossPr): """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr self.List_User = [] # Initialisation de l'event Id self.Last_event_Id = -1 # Liste des films self.MovieList = self.serverProxy.getMovieList() self.Nbr_Movie = 0 # Liste des identifiants des movies rooms self.listeIdRoom = [] # Initialisation des buffers qu'on va utiliser pour le Response Event self.buf_switch = bytearray(0) self.buf_new_msg = bytearray(0) self.buf_logout = bytearray(0) self.buf_new_user = bytearray(0) # Liste des timers self.timer = [] # Dernier paquet reçu self.datagram = bytearray(0) # Derniière réponse envoyée self.response = bytearray(0) # L'adresse et le port du dernier utilisateur qui envoyé un paquet self.host_port = (0, 0) self.bool = False def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport # Méthode qui incrémente l'event Id def increment_Event_Id(self): if (self.Last_event_Id > 65536): self.Last_event_Id = 0 else: self.Last_event_Id += 1 # Méthode à exécuter si on ne reçoit pas de ping après 60s def verifyResponse(self, parameter): user_id_list = [] for i in self.serverProxy.getUserList(): user_id_list.append(i.userId) if parameter in user_id_list: increment_Event_Id(self) # Formation du paquet à envoyer dans le response event (evenement: suppression d'un utilisateur) # Determination de l'identifiant du room dans laquelle l'utilisateur se trouve Room_Name = self.serverProxy.getUserById(parameter).userChatRoom if Room_Name == ROOM_IDS.MAIN_ROOM: RoomId = 0 else: RoomId = self.serverProxy.getMovieByTitle(Room_Name).movieId # Last_Event_Id a = (self.Last_event_Id & int('111111111111111100000000', 2)) >> 8 b = (self.Last_event_Id) & 255 # Message_data buf = struct.pack('!HBBBB', a, b, 0x4, RoomId, parameter) # Supression de l'utilisateur' self.serverProxy.removeUser( self.serverProxy.getUserById(parameter).userName) self.buf_logout += buf # Formation petit à petit du buf à envoyer dans le response event pour l'event logout def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ # Récupération de l'entete (Message_Type, Seq_Number, User_ID, Message_Length) = struct.unpack('!BHBH', datagram[:6]) # Si on reçoit le meme paquet une deuxième fois if self.datagram == datagram: if host_port == self.host_port: self.transport.write(self.response, host_port) else: self.datagram = datagram self.host_port = host_port #----------------------------- RESPONSE-LOGIN ----------------------------------------------- if Message_Type == 0x00: # Recupération du data (UL, Username) = struct.unpack( '!B' + str(Message_Length - 1) + 's', datagram[6:]) # Récupération de la liste des utilisateurs self.List_User = self.serverProxy.getUserList() # Détermination du status_Code i = 0 for n in self.List_User: if n.userName == Username.decode( 'utf-8' ): # Si le nom d'utilisateur existe déjà dans la liste i = 1 User_Id = 0 if i == 1: Status_Code = 4 # USERNAME-NOT_AVIALABLE else: Status_Code = 0 # SUCCESS self.List_User.append(Username) # Ajout de l'utilisateur à la liste et génération de User_ID User_Id = self.serverProxy.addUser( Username.decode('utf-8'), ROOM_IDS.MAIN_ROOM, userAddress=host_port[0]) # Incrémentation de l'event Id increment_Event_Id(self) a = (self.Last_event_Id & int('111111111111111100000000', 2)) >> 8 b = (self.Last_event_Id) & 255 # Message_data buf = struct.pack('!HBBBBB' + str(UL) + 's', a, b, 0x2, 0x0, User_Id, UL, Username) self.buf_new_user += buf # Formation petit a petit du buf a envoyer dans le response event pour l'event new user if User_Id > 255: Status_Code = 2 # TOO_MANY_USERS # Construction et envoi de la réponse a = (self.Last_event_Id & int('111111111111111100000000', 2)) >> 8 b = (self.Last_event_Id) & 255 Response_Login = struct.pack('!BHBHBBHB', 0x01, Seq_Number, 0, 5, Status_Code, User_Id, a, b) self.transport.write(Response_Login, host_port) self.response = Response_Login # Lancement du Timer self.timer.append( reactor.callLater(60, self.verifyResponse, User_Id)) #----------------------------- RESPONSE-ROOMS ----------------------------------------------- if Message_Type == 0x08: # Récupération du data (First_Room_Id, Nbr_Rooms) = struct.unpack('!BB', datagram[6:]) NbrMovie = 0 # Calcul du Message Length Msg_Length = 0 for i in self.MovieList: if (i.movieId >= First_Room_Id) and ( i.movieId <= First_Room_Id + Nbr_Rooms): Msg_Length += len(i.movieTitle) + 9 NbrMovie += 1 # Formation de l'entete UserList = self.serverProxy.getUserList() buf = bytearray(0) buf += struct.pack('!BHBHB', 9, Seq_Number, 0, Msg_Length + 1, NbrMovie) # Formation du message self.MovieList = self.serverProxy.getMovieList() for i in self.MovieList: # Parcours de la liste des videos disponibles if (i.movieId >= First_Room_Id) and ( i.movieId <= First_Room_Id + Nbr_Rooms): (addr_IP, Movie_Port) = self.serverProxy.getMovieAddrPort( i.movieTitle) Video_Name = i.movieTitle Nbr = 0 for j in UserList: if Video_Name == j.userChatRoom: Nbr += 1 ip = addr_IP.split('.') RNL = len(i.movieTitle) # Construction de la réponse buf += struct.pack('!B4B', i.movieId, int(ip[0]), int(ip[1]), int(ip[2]), int(ip[3])) buf += struct.pack('!HB' + str(RNL) + 'sB', Movie_Port, RNL, (i.movieTitle).encode('utf-8'), Nbr) self.transport.write(buf, host_port) self.response = buf #----------------------------- RESPONSE_USERS ----------------------------------------------- if Message_Type == 0x0A: # Récupération du data (First_User_Id, Nbr_Users, Room_Id) = struct.unpack('!BBB', datagram[6:]) # Récupération de la liste des utilisateurs UserList = self.serverProxy.getUserList() LenUserList = len(self.serverProxy.getUserList()) self.Nbr_Movie = len(self.MovieList) n = 0 # Si l'utilisateur demande la liste d'une room précise if Room_Id != 0x00: # calcul de la longueur du message Msg_Length = 0 Nbr = 0 # Nombre d'utlisateurs dans le movie room e = 0 Video_Name = self.serverProxy.getMovieById( Room_Id).movieTitle for i in UserList: if Video_Name == i.userChatRoom: Msg_Length += len(i.userName) + 3 Nbr += 1 # construction de l'entete buf = bytearray(0) buf = struct.pack('!BHBHB', 11, Seq_Number, 0, Msg_Length + 1, Nbr) for i in UserList: if Video_Name == i.userChatRoom: if (i.userId >= First_User_Id) and ( i.userId < First_User_Id + Nbr_Users): UL = len(i.userName) buf += struct.pack( '!BB' + str(UL) + 'sB', i.userId, UL, (i.userName).encode('utf-8'), Room_Id) # si l'utilisateur demande le liste des utilisateurs dans toutes les rooms if Room_Id == 0x00: # calcul de la longueur du message Msg_Length = 0 buf = bytearray(0) for i in UserList: Msg_Length += len(i.userName) + 3 # Entete buf = struct.pack('!BHBHB', 11, Seq_Number, 0, Msg_Length + 1, len(UserList)) e = 0 # Récupération des listes des clients pour chaque movie room for i in self.MovieList: if i.movieId not in self.listeIdRoom: self.listeIdRoom.append(i.movieId) listeIdRoom = self.listeIdRoom listeIdRoom.append(0) for RoomId in listeIdRoom: if RoomId != 0: Video_Name = self.serverProxy.getMovieById( RoomId).movieTitle for i in UserList: if (Video_Name == i.userChatRoom): if (i.userId >= First_User_Id) and ( i.userId < First_User_Id + Nbr_Users): UL = len(i.userName) buf += struct.pack( '!BB' + str(UL) + 'sB', i.userId, UL, (i.userName).encode('utf-8'), RoomId) elif e == 0: e += 1 for i in UserList: if (i.userChatRoom == ROOM_IDS.MAIN_ROOM): if (i.userId >= First_User_Id) and ( i.userId < First_User_Id + Nbr_Users): UL = len(i.userName) # struct.pack_into('!BB'+str(UL)+'sB',buf,7+n,i.userId,UL,(i.userName).encode('utf-8'),0) # n += 3 + UL buf += struct.pack( '!BB' + str(UL) + 'sB', i.userId, UL, (i.userName).encode('utf-8'), RoomId) self.transport.write(buf, host_port) self.response = buf #----------------------------- RESPONSE_MESSAGE ----------------------------------------------- if Message_Type == 0x0E: # Récupération du data (Room_Id, Msg_Length, Message_Content) = struct.unpack( '!BH' + str(Message_Length - 3) + 's', datagram[6:]) # Détermination de roomID du client Room_Name = ( self.serverProxy.getUserById(User_ID)).userChatRoom if Room_Name == ROOM_IDS.MAIN_ROOM: RoomId = 0 else: RoomId = self.serverProxy.getMovieByTitle( Room_Name).movieId # Détermination du Status_Code if int(Room_Id) == RoomId: Status_Code = 0 increment_Event_Id(self) a = (self.Last_event_Id & int('111111111111111100000000', 2)) >> 8 b = (self.Last_event_Id) & 255 # Construction de la réponse buf = bytearray(0) buf = struct.pack('!HBBBBH' + str(Msg_Length) + 's', a, b, 0x1, Room_Id, User_ID, Msg_Length, Message_Content) self.buf_new_msg += buf # Formation petit a petit du buf a envoyer dabs le response event pour l'event new message elif int(Room_Id) != RoomId: if RoomId in self.listeIdRoom: Status_Code = 3 else: Status_Code = 2 else: Status_Code = 1 Response_Message = struct.pack('!BHBHB', 0x0F, Seq_Number, 0, 1, Status_Code) self.transport.write(Response_Message, host_port) self.response = Response_Message #----------------------------- RESPONSE_PING ----------------------------------------------- if Message_Type == 0x04: self.timer[User_ID - 1].reset(60) # Récupération du data (LastEventId_a, LastEventId_b, Room_Id) = struct.unpack('!HBB', datagram[6:]) a = (self.Last_event_Id & int('111111111111111100000000', 2)) >> 8 b = (self.Last_event_Id) & 255 # Construction et envoi de la réponse Response_Ping = struct.pack('!BHBHHB', 0x05, Seq_Number, 0, 3, a, b) self.transport.write(Response_Ping, host_port) self.response = Response_Ping #----------------------------- RESPONSE_SWITCH_ROOM ----------------------------------------- if Message_Type == 0x0C: # Récupération du data (Room_Id) = struct.unpack('!B', datagram[6:]) userlist = self.serverProxy.getUserList() # Détermination du room ID du client =RoomId Room_Name = self.serverProxy.getUserById(User_ID).userChatRoom if Room_Name == ROOM_IDS.MAIN_ROOM: RoomId = 0 else: RoomId = self.serverProxy.getMovieByTitle( Room_Name).movieId # Détermination du Status_Code if Room_Id[0] == RoomId: Status_Code = 1 elif (RoomId != 0) and (Room_Id[0] != 0): Status_Code = 1 elif (Room_Id[0] not in self.listeIdRoom): Status_Code = 1 else: Status_Code = 0 if (Room_Id[0] != 0): for i in userlist: #on verifie si il y a encore des clients dans la room if self.serverProxy.getMovieById( Room_Id[0]).movieTitle == i.userChatRoom: p = 1 else: p = 0 if p == 0: #si il n' y a plus personne dans la room: Start streaming lorsque le client quitte le main room vers un movie room self.serverProxy.startStreamingMovie( self.serverProxy.getMovieById(int( Room_Id[0])).movieTitle) else: for i in userlist: #on verifie si il y a encore des clients dans la room if self.serverProxy.getMovieById( RoomId).movieTitle == i.userChatRoom: p = 1 else: p = 0 if p == 0: # si il n' y a plus personne dans la room: Stop streaming lorsque le client quitte un movie room self.serverProxy.stopStreamingMovie( self.serverProxy.getMovieById( int(RoomId)).movieTitle) if Room_Id[0] == 0: self.serverProxy.updateUserChatroom( self.serverProxy.getUserById(User_ID).userName, ROOM_IDS.MAIN_ROOM) else: self.serverProxy.updateUserChatroom( self.serverProxy.getUserById(User_ID).userName, self.serverProxy.getMovieById(int( Room_Id[0])).movieTitle) increment_Event_Id(self) a = (self.Last_event_Id & int('111111111111111100000000', 2)) >> 8 b = (self.Last_event_Id) & 255 #buf = struct.pack('!HBBBBB',a,b,0x3,Room_Id[0],User_ID,int(Room_Id[0])) buf = struct.pack('!HBBBBB', a, b, 0x3, RoomId, User_ID, Room_Id[0]) self.buf_switch += buf #formation petit a petit du buf a envoyer dabs le response event pour l'event switch room Response_Switch_Room = struct.pack('!BHBHB', 0x0D, Seq_Number, 0, 1, Status_Code) self.transport.write(Response_Switch_Room, host_port) self.response = Response_Switch_Room #----------------------------- RESPONSE_LOGOUT ----------------------------------------------- if Message_Type == 0x02: # Détermination du room Id du client Room_Name = self.serverProxy.getUserById(User_ID).userChatRoom if Room_Name == ROOM_IDS.MAIN_ROOM: RoomId = 0 else: RoomId = self.serverProxy.getMovieByTitle( Room_Name).movieId if RoomId == 0: Status_Code = 0 # Supression du client de la liste self.serverProxy.removeUser( self.serverProxy.getUserById(User_ID).userName) increment_Event_Id(self) a = (self.Last_event_Id & int('111111111111111100000000', 2)) >> 8 b = (self.Last_event_Id) & 255 buf = struct.pack('!HBBBB', a, b, 0x4, RoomId, User_ID) self.buf_logout += buf #formation petit a petit du buf a envoyer dans le response event pour l'event logout self.timer[User_ID - 1].cancel() userList = self.serverProxy.getUserList() else: Status_Code = 1 Response_Logout = struct.pack('!BHBHB', 0x03, Seq_Number, 0, 1, Status_Code) self.transport.write(Response_Logout, host_port) self.response = Response_Logout #----------------------------- RESPONSE_EVENTS ----------------------------------------------- if Message_Type == 0x06: # Récupération du data (LastEventId_a, LastEventId_b, Nbr_Events, Room_Id) = struct.unpack('!HBBB', datagram[6:]) LastEventId = (LastEventId_a << 8) | LastEventId_b buf = bytearray(0) event_nbr = 0 v = 0 i = 0 p = 0 # Si l'utilisateur demande les événements d'un room précis if Room_Id != 0: # Event : new message while (i <= len(self.buf_new_msg) - 1): taille_msg = struct.unpack( '!H', self.buf_new_msg[i + 6:i + 8]) p = int(taille_msg[0]) + 8 v += p if (self.buf_new_msg[i + 4] == Room_Id) and ( ((self.buf_new_msg[i] << 16) | (self.buf_new_msg[i + 1] << 8) | self.buf_new_msg[i + 2]) > LastEventId): buf += self.buf_new_msg[i:v] event_nbr += 1 i += p # Event : new user v = 0 i = 0 p = 0 while (i <= len(self.buf_new_user) - 1): ul = self.buf_new_user[i + 6] p = int(ul) + 7 v += p if (((self.buf_new_user[i] << 16) | (self.buf_new_user[i + 1] << 8) | self.buf_new_user[i + 2]) > LastEventId): buf += self.buf_new_user[i:v] event_nbr += 1 i += p # Event : logout p = 0 i = 0 v = 0 while (i <= len(self.buf_logout) - 1): p = 6 v += p if (((self.buf_logout[i] << 16) | (self.buf_logout[i + 1] << 8) | self.buf_logout[i + 2]) > LastEventId): #if (self.buf_logout[i+4] == Room_Id) and (((self.buf_logout[i]<<16) | (self.buf_logout[i+1]<<8) | self.buf_logout[i+2]) > LastEventId ): buf += self.buf_logout[i:v] event_nbr += 1 i += p # Event : switch room i = 0 v = 0 p = 0 while (i <= len(self.buf_switch) - 1): p = 7 v += p if (self.buf_switch[i + 6] == Room_Id) and ( ((self.buf_switch[i] << 16) | (self.buf_switch[i + 1] << 8) | self.buf_switch[i + 2]) > LastEventId): buf += self.buf_switch[i:v] event_nbr += 1 if (self.buf_switch[i + 6] == 0) and ( ((self.buf_switch[i] << 16) | (self.buf_switch[i + 1] << 8) | self.buf_switch[i + 2]) > LastEventId): buf += self.buf_switch[i:v] event_nbr += 1 i += p buf1 = struct.pack('!BHBHB', 0x07, Seq_Number, 0, len(buf) + 1, event_nbr) buf1 += buf # Si l'utilisateur demande tous les événements else: # Event : new message p = 0 i = 0 while (i <= len(self.buf_new_msg) - 1): taille_msg = struct.unpack( '!H', self.buf_new_msg[i + 6:i + 8]) p = int(taille_msg[0]) + 8 v += p if (((self.buf_new_msg[i] << 16) | (self.buf_new_msg[i + 1] << 8) | self.buf_new_msg[i + 2]) > LastEventId): buf += self.buf_new_msg[i:v] i += p # Event : new user p = 0 i = 0 v = 0 while (i <= len(self.buf_new_user) - 1): ul = self.buf_new_user[i + 6] p = int(ul) + 7 v += p if (((self.buf_new_user[i] << 16) | (self.buf_new_user[i + 1] << 8) | self.buf_new_user[i + 2]) > LastEventId): buf += self.buf_new_user[i:v] i += p # Event : logout p = 0 i = 0 v = 0 while (i <= len(self.buf_logout) - 1): p = 6 v += p if (((self.buf_logout[i] << 16) | (self.buf_logout[i + 1] << 8) | self.buf_logout[i + 2]) > LastEventId): buf += self.buf_logout[i:v] i += p # Event : switch room p = 0 i = 0 v = 0 while (i <= len(self.buf_switch) - 1): p = 7 v += p if (((self.buf_switch[i] << 16) | (self.buf_switch[i + 1] << 8) | self.buf_switch[i + 2]) > LastEventId): buf += self.buf_switch[i:v] i += p p = 0 i = 0 buf1 = struct.pack('!BHBHB', 0x07, Seq_Number, 0, len(buf) + 1, Nbr_Events) buf1 += buf self.transport.write(buf1, host_port) self.response = buf1 pass
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.num_sequence = 0 self.num_sequence_server = 0 self.users = [] self.numDeconection = 0 self.numJoinRoom = 0 self.numMainRoom = 0 self.listUser = [] self.movies = [] self.filattente = [] self.username = '' self.num_error = 0 self.error_count = 0 self.retrans_server = 0 # fonction pour verifier si on a recu un ack def traitementAck(self, numSeq): for p in self.filattente: if (p[0] == numSeq): p[2] = 1 #print(p) #print(self.filattente) #if numSeq==self.numDeconection and numSeq>=1: # self.clientProxy.applicationQuit() # break self.num_sequence += 1 print(self.num_sequence) print('******************ack envoye par le serveur') self.filattente.remove(p) #fonction pour envoyer le paquet si jamais on a toujours pas recu d ack def sendAndWait(self, host_port): for p in self.filattente: if (p[4] == host_port): print(self.filattente) if ( p[1] <= 7 ): # 6+1 correspond au nombre maximum de fois qu'on doit ramener un paquet if (p[2] == 0): self.transport.write(p[3], host_port) p[1] += 1 print(self.filattente) print('nombre de message envoye:' + str(p[1])) reactor.callLater(1, self.sendAndWait, host_port) elif (p[2] == 1): print('Le paquet a ete aquitte') self.filattente.remove(p) else: print('Le paquet envoye est perdu') self.filattente.remove(p) #EFFECTUER LES COMMANDE DE DECONNECTION ET AFFICAHGE DE MESSAGE DE DECONNEXION #DANS LE CAS DUNE CONNEXION, DECONNEXION, CHANGEMNT DE ROOM self.clientProxy.connectionRejectedONE( "\nLa connexion au serveur à été rompue\nVous devez vous reconnecter" ) #self.clientProxy.applicationQuit() def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ """ #Connecting to the server #self.transport.connect(self.serverAddress, self.serverPort) #The message length taille du paquet msg_length = 4 + len(userName.encode('utf-8')) #Combining the sequence number and the type #num_seq = self.num_sequence << 4 #connection_type = 1 seq_and_connection = util.prepare_header(self.num_sequence,1) #print(bin(seq_and_connection)) #Packing the username length_username = str(len(userName)) buf = struct.pack('!hh'+length_username+'s', msg_length, seq_and_connection, userName.encode('utf-8')) self.transport.write(buf, (self.serverAddress, self.serverPort)) """ self.username = userName buf = util.format_login(userName) self.transport.write(buf, (self.serverAddress, self.serverPort)) self.filattente.append( [0, 1, 0, buf, (self.serverAddress, self.serverPort)]) print(self.filattente) reactor.callLater(1, self.sendAndWait, (self.serverAddress, self.serverPort)) moduleLogger.debug('loginRequest called with username=%s', userName) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ buf = util.format_chat(self.num_sequence, 9, self.username, message) self.transport.write(buf, (self.serverAddress, self.serverPort)) #self.transport.write(buf, (self.serverAddress, self.serverPort)) #self.transport.write(buf, (self.serverAddress, self.serverPort)) self.filattente.append([ self.num_sequence, 1, 0, buf, (self.serverAddress, self.serverPort) ]) #print(self.filattente) reactor.callLater(1, self.sendAndWait, (self.serverAddress, self.serverPort)) #self.transport.write(buf, (self.serverAddress, self.serverPort)) #self.transport.write(buf, (self.serverAddress, self.serverPort)) print(util.get_username_fromChat(buf), " :") print(util.get_message_test(buf)) pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. #A faire envoyer demande de deconnexion buf2=util.format_select_film(roomName, self.num_sequence) print("jeveuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupannnnnnnnnnnn") self.transport.write(buf2, (self.serverAddress, self.serverPort)) self.filattente.append([self.num_sequence,1,0,buf2,(self.serverAddress, self.serverPort)]) reactor.callLater(1,self.sendAndWait,(self.serverAddress, self.serverPort)) self.numJoinRoom=self.num_sequence RETRANSMISSION PAS ENCORE GERER+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+ #print("jeeeeeeeeeeveuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupannnnnnnnnnnn") .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ if roomName != ROOM_IDS.MAIN_ROOM: buf2 = util.format_select_film(roomName, self.num_sequence) print( "jeveuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupannnnnnnnnnnn" ) self.transport.write(buf2, (self.serverAddress, self.serverPort)) self.numJoinRoom = self.num_sequence self.filattente.append([ self.num_sequence, 1, 0, buf2, (self.serverAddress, self.serverPort) ]) reactor.callLater(1, self.sendAndWait, (self.serverAddress, self.serverPort)) #self.clientProxy.joinRoomOKONE() else: buf2 = util.format_header(4, self.num_sequence) #print("jeveuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupannnnnnnnnnnn") self.transport.write(buf2, (self.serverAddress, self.serverPort)) self.numJoinRoom = self.num_sequence self.filattente.append([ self.num_sequence, 1, 0, buf2, (self.serverAddress, self.serverPort) ]) reactor.callLater(1, self.sendAndWait, (self.serverAddress, self.serverPort)) #self.clientProxy.joinRoomOKONE() #""" #self.clientProxy.joinRoomOKONE() pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ #""" #A faire envoyer demande de deconnexion buf2 = util.format_header(2, self.num_sequence) print("jeveuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupannnnnnnnnnnn") self.transport.write(buf2, (self.serverAddress, self.serverPort)) self.numDeconection = self.num_sequence self.filattente.append([ self.num_sequence, 1, 0, buf2, (self.serverAddress, self.serverPort) ]) reactor.callLater(1, self.sendAndWait, (self.serverAddress, self.serverPort)) #""" print( "jeeeeeeeeeeveuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupannnnnnnnnnnn" ) #self.clientProxy.applicationQuit() pass def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ packet_type = util.get_type(datagram) #packet_numSeq= util.get_numSequence (datagram) if packet_type == 0: #Reception d'un ACK packet_numSeq = util.get_numSequence(datagram) self.traitementAck(packet_numSeq) print("*******************ACK DU SERVER", packet_numSeq) print("************************MON NUM SEQ", self.num_sequence) print("****-----------------*ACK CONNEXION REQUEST****") if packet_numSeq == self.numDeconection and packet_numSeq > 0: self.clientProxy.applicationQuit() if packet_numSeq == self.numJoinRoom and packet_numSeq > 0: self.clientProxy.joinRoomOKONE() packet_numSeq = util.get_numSequence(datagram) if self.num_sequence_server == packet_numSeq and packet_type != 0: self.retrans_server = 0 self.error_count = 0 # if packet_type == 5: # Reception liste des films buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) self.num_sequence_server += 1 #sending the ACK message-----------------------APRES ENVOIE ACK +1 AU NUM SÉQUENCE DU CLIENT AFAIRE print("*******************ACK DU SERVER", packet_numSeq) print("************************MON NUM SEQ", self.num_sequence) self.movies = util.get_moviesList(datagram) print(self.movies) """""" """""" """""" """""" #self.clientProxy.initCompleteONE(self.listUser,self.movies) #print(packet_type) """""" """""" elif packet_type == 6: # Reception liste des Users if self.listUser == []: buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) self.num_sequence_server += 1 print( "********************************Reception USER**************************" ) #print(util.format_usersList(self.usersList,self.serverProxy.getMovieList())) print(datagram) self.listUser = util.get_usersList(datagram, self.movies) #self.filattente.append([2,1,0,listUser,(host_port[0], host_port[1])]) #self.transport.write(listUser, (host_port[0], host_port[1])) print(self.listUser) print(self.movies) #print(listUser) self.clientProxy.initCompleteONE(self.listUser, self.movies) #print(packet_type) else: buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) self.num_sequence_server += 1 self.listUser = util.get_usersList(datagram, self.movies) self.clientProxy.setUserListONE(self.listUser) elif packet_type == 7: # -------------- ACCEPTATION DE CONNECTION #self.users.append(self.username) #print(packet_type) # self.clientProxy.initCompleteONE(self.users,self.movies) buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) self.num_sequence_server += 1 print("*******************ACK DU SERVER", packet_numSeq) print("***********", packet_numSeq) elif packet_type == 8: buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) #self.num_sequence_server+=1 self.clientProxy.connectionRejectedONE( "\nLe pseudo que vous avez entrez est deja utilisé, ou trop long.\nVeuillez réessayer avec un autre" ) elif packet_type == 9: # print("Yorobo") buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) self.num_sequence_server += 1 sender = util.get_username_fromChat(datagram) message = util.get_message(datagram) if sender != self.username: self.clientProxy.chatMessageReceivedONE(sender, message) #""" elif self.num_sequence_server != packet_numSeq and packet_type != 0: #else: """ print("+-+-+-+-+-+-Jai recu un doublon on dirait+-+-+-+-+-+-+-") #self.num_sequence_client buf=util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) #self.num_sequence_client+=1 """ if packet_numSeq != self.num_error: if self.error_count != 7: self.retrans_server = 0 self.error_count = +1 print( "+-+-+-+-+-+-Jai recu un numSeq innatendue on dirait+-+-+-+-+-+-+-" ) print(packet_numSeq) print(self.num_sequence_server) #self.num_sequence_client buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) #self.num_sequence_client+=1 elif self.error_count == 7: self.clientProxy.applicationQuit() elif packet_numSeq == self.num_error: if self.retrans_server != 7 and self.error_count != 7: self.error_count = +1 self.retrans_server += 1 print( "+-+-+-+-+-+-Jai recu un doublon on dirait+-+-+-+-+-+-+-" ) #self.num_sequence_client buf = util.format_ack(packet_numSeq) self.transport.write(buf, (host_port[0], host_port[1])) #self.num_sequence_client+=1 elif self.retrans_server == 7 or self.error_count == 7: self.clientProxy.applicationQuit() self.num_error = packet_numSeq #""" #""" print("num sequence du server attendue", self.num_sequence_server) print("num sequence du client ", self.num_sequence) pass
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address (or the name) of the c2w server. .. attribute:: serverPort The port number used by the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ self.serverAddress = serverAddress self.serverPort = serverPort self.clientProxy = clientProxy self.lossPr = lossPr self.nbtime = 0 self.movielist='' self.USERID=0 self.userName='' self.roomName='' self.flag=0 self.obj=None self.flaguser=0 self.flagmovie=0 self.clientsequencenb=0 self.userListresult=[] self.movieListresult=[] self.movieListstock=[] self.userListstock=[] self.location=-1 self.roomId=0 self.message='' self.count=0 self.prefrg=0 self.moviefrg='' self.movielengthfrg=0 self.userlengthfrg=0 self.previousserverseqnb=-1 def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendRequest(self, buf, adress): """ :param line: the text of the question from the user interface :type line: string This function must send the request to the server. """ self.transport.write(buf,adress) #### send login request def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The controller calls this function as soon as the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) self.userName=userName lenUserName=len(userName) s=struct.Struct('!BBBBH'+str(lenUserName)+'s') packdata=s.pack(*(3,0,0,0,lenUserName, userName)) adress=(self.serverAddress,self.serverPort) self.sendRequest(packdata,adress) self.nbtime=self.nbtime+1 ####Set the time out if(self.nbtime<=3): self.timeout(userName,0) if(self.nbtime==4): self.nbtime=0 ### Send movie list ACK def sendMovieListAck(self,sequencenb,userid): #moduleLogger.debug('loginRequest called with username=%s', ) buf=ctypes.create_string_buffer(6) struct.pack_into('>BBBBH',buf,0,79,sequencenb,userid,0,0) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) ### Send user list ACK def sendUserListAck(self,sequencenb,userid): #moduleLogger.debug('loginRequest called with username=%s', ) buf=ctypes.create_string_buffer(6) struct.pack_into('!BBBBH',buf,0,87,sequencenb,userid,0,0) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) ### Send message ACK def sendmessageAck(self,serversequencenb,userid,roomtype): buf=ctypes.create_string_buffer(6) if(roomtype==0): struct.pack_into('!BBBBH',buf,0,112,serversequencenb,userid,0,0) if(roomtype==1): struct.pack_into('!BBBBH',buf,0,113,serversequencenb,userid,0,0) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) ###### Send chat message def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called **by the controller** when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ length=len(message) if(length<140): self.message=message buf=ctypes.create_string_buffer(6+length) if(self.location==0): #### location=0 mean user is being mainroom, and send chat message struct.pack_into('!BBBBH'+str(length)+'s',buf,0,4,self.clientsequencenb,self.USERID,0,length,message) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) if(self.location==1): #### location=1 mean user is already in mainroom, and send chat message struct.pack_into('!BBBBH'+str(length)+'s',buf,0,5,self.clientsequencenb,self.USERID,self.roomId,length,message) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) self.nbtime=self.nbtime+1 #### set the timeout if(self.nbtime<=3): self.timeout(message,2) if(self.nbtime==4): self.nbtime=0 def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called **by the controller** when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ moduleLogger.debug('loginRequest called with username=%s', ) if(self.location==0): #### location=0 user is being mainroom, and send room request for tuple in self.movieListstock: ##### take roomid from roomname if tuple[0]==roomName: roomid=tuple[3] self.roomName=roomName self.roomId = roomid buf=ctypes.create_string_buffer(6) struct.pack_into('!BBBBH',buf,0,35,self.clientsequencenb,self.USERID,roomid,0) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) if(self.location==1): #### location=1 mean user is already in mainroom, and send request to leave movingroom buf=ctypes.create_string_buffer(6) struct.pack_into('!BBBBH',buf,0,33,self.clientsequencenb,self.USERID,0,0) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) self.nbtime=self.nbtime+1 if(self.nbtime<=3): self.timeout(roomName,1) if(self.nbtime==4): self.nbtime=0 #### send request to leave out application def sendLeaveSystemRequestOIE(self): """ Called **by the controller** when the user has clicked on the leave button in the main room. """ pass buf=ctypes.create_string_buffer(6) struct.pack_into('!BBBBH',buf,0,60,self.clientsequencenb,self.USERID,0,0) adress=(self.serverAddress,self.serverPort) self.sendRequest(buf,adress) self.nbtime=self.nbtime+1 if(self.nbtime<=3): self.timeout(0,3) if(self.nbtime==4): self.nbtime=0 #### timeout function def timeout(self,data,functionid): if(self.nbtime==3): self.obj.cancel() if(functionid==0): self.clientProxy.connectionRejectedONE('connection failed,please try again') ##if in case mainroom to movieroom, we think client don't quit if(self.flag==0): if(functionid==0): self.obj=reactor.callLater(3,self.sendLoginRequestOIE,data) if(functionid==1): self.obj=reactor.callLater(3,self.sendJoinRoomRequestOIE,data) if(functionid==2): self.obj=reactor.callLater(3,self.sendChatMessageOIE,data) if(functionid==3): self.obj=reactor.callLater(3,self.sendLeaveSystemRequestOIE) ### data recived def datagramReceived(self, buf, (host, port)): """ :param string datagram: the payload of the UDP packet. :param host: the IP address of the source. :param port: the source port. Called **by Twisted** when the client has received a UDP packet. """ adress=(host, port) tuple1=struct.unpack_from('>B',buf[0]) frg=self.getbit(tuple1[0],1,1) ack=self.getbit(tuple1[0],2,2) mestype=self.getbit(tuple1[0],3,6) roomtype=self.getbit(tuple1[0],7,8) tuple2=struct.unpack_from('>B',buf[1]) serversequencenb=tuple2[0] tuple3=struct.unpack_from('>B',buf[2]) userid=tuple3[0] tuple4=struct.unpack_from('>B',buf[3]) destid=tuple4[0] tuple5=struct.unpack_from('>H',buf[4:6]) length=tuple5[0] if (ack==1 and mestype==8 and self.location==0): #### use when receiving room request ACK port=struct.unpack_from('>H',buf[6:8])[0] ip1 = struct.unpack_from('>B',buf[8])[0] ip2 = struct.unpack_from('>B',buf[9])[0] ip3 = struct.unpack_from('>B',buf[10])[0] ip4 = struct.unpack_from('>B',buf[11])[0] else: tuple6=struct.unpack_from(str(length)+'s',buf[6:]) data=tuple6[0] if(ack==1 and mestype==0): ## login ack self.flag=1 #self.timeout(data) self.nbtime=0 self.obj.cancel() self.USERID=userid self.flag=0 if(ack==1 and mestype==1): ## chat message ack self.flag=1 #self.timeout(data) self.nbtime=0 self.obj.cancel() self.clientsequencenb=self.clientsequencenb+1 self.flag=0 if(ack==0 and mestype==12): #receive the forward message self.sendmessageAck(serversequencenb,self.USERID,roomtype) #self.count=self.count+1 userid=int(data[0]) for user in self.userListstock: if user[0]==userid: userName=user[1] self.clientProxy.chatMessageReceivedONE(userName, data[1:]) if(ack==1 and mestype==14): ### error message errmes=struct.unpack_from('>B',data[0])[0] if(errmes==6): self.clientProxy.connectionRejectedONE('username not available') elif(errmes==4): self.obj.cancel() if(mestype==3 and ack==0): ## receive the movielist given by the server,client send movielistack to server self.sendMovieListAck(serversequencenb,userid) ### flag indicate that client has already received the movie list if(frg==1 and serversequencenb > self.previousserverseqnb): self.prefrg=1 self.moviefrg=self.moviefrg+data self.movielengthfrg=self.movielengthfrg+length self.previousserverseqnb=serversequencenb if (frg==0 and self.prefrg==1 and serversequencenb > self.previousserverseqnb): self.moviefrg=self.moviefrg+data self.prefrg=0 self.movielist=self.moviefrg self.movielistlength=self.movielengthfrg+length self.previousserverseqnb=serversequencenb self.flagmovie=1 if(frg==0 and self.prefrg==0): self.movielist=data self.movielistlength=length self.flagmovie=1 if(mestype==5 and ack==0 and userid==self.USERID): ##if it is the userlist given by the server,client send userlistack to server(from mainroom or movieroom) self.sendUserListAck(serversequencenb,userid) ##when the user is already in mainroom or movieroom, he receives userlist update if(frg==1 and serversequencenb > self.previousserverseqnb): ## we use the server's sequence nb to distinguish the different fragmentation self.prefrg=1 self.userfrg=self.userfrg+data self.userlengthfrg=self.userlengthfrg+length self.previousserverseqnb=serversequencenb if (frg==0 and self.prefrg==1 and serversequencenb > self.previousserverseqnb): self.userfrg=self.userfrg+data self.prefrg=0 self.userlist=self.userfrg self.userlistlength=self.userlengthfrg+length self.previousserverseqnb=serversequencenb self.flaguser=1 ###flag indicate that client has already received the userlist if(frg==0 and self.prefrg==0): self.userlist=data self.userlistlength=length self.flaguser=1 if(self.location==0 or self.location==1): self.flagmovie=1 if(self.flaguser==1 and self.flagmovie==1): ## to check that client has received both movielist and userlist i=0 j=0 self.userListstock=[] movieListresult=[] while(i<self.userlistlength): namelength=struct.unpack_from('>B',self.userlist[i])[0] userid=struct.unpack_from('>B',self.userlist[i+1])[0] status=struct.unpack_from('>B',self.userlist[i+2])[0] p=i i=i+3+namelength usernameoflist=struct.unpack_from(str(i-p-3)+'s',self.userlist[p+3:i])[0]#self.userlist[p+3:i-1] userid=struct.unpack_from('>B',self.userlist[p+1])[0] if(status==128): tuple1=(userid,usernameoflist,ROOM_IDS.MAIN_ROOM) if(status==0): tuple1=(userid,usernameoflist,ROOM_IDS.MOVIE_ROOM) if(destid==self.roomId): # for broadcast in a specific movieroom for tuple in self.movieListstock: if tuple[3]==destid: #just get one movie tuple1=(userid,usernameoflist,tuple[0]) # get user list with the movietitle with client in the movingroom self.userListresult.append((tuple1[1],tuple1[2])) self.userListstock.append(tuple1) if(self.location==0 or self.location==1): self.clientProxy.setUserListONE(self.userListresult) if(self.location==-1): #### location=-1 for the first time client receive movie list after login sucessful while(j<self.movielistlength): namelength=struct.unpack_from('>B',self.movielist[j])[0] roomid=struct.unpack_from('>B',self.movielist[j+1])[0] p=j j=j+2+namelength movietitleoflist=struct.unpack_from(str(j-p-2)+'s',self.movielist[p+2:j])[0] tuplesafe=(movietitleoflist,1,1,roomid) tuple=(movietitleoflist,1,1) self.movieListstock.append(tuplesafe) self.movieListresult.append(tuple) ###############use for and display movie and user list self.clientProxy.initCompleteONE(self.userListresult, self.movieListresult) self.userListresult=[] if roomtype==0: self.location=0 #successully login and receive userlist and movielist self.flaguser=0 self.flagmovie=0 # ACK leave movie room if (ack==1 and mestype==8 and self.location==1): self.flag=1 self.nbtime=0 #self.timeout(data) self.obj.cancel() self.clientProxy.joinRoomOKONE() self.location=0 self.flag=0 else: if(ack==1 and mestype==8 and self.location==0): ## change room self.nbtime=0 self.flag=1 self.obj.cancel() self.clientsequencenb=self.clientsequencenb+1 self.location=1 ipAdresslist= [str(ip1),str(ip2),str(ip3),str(ip4)] point="." ipAdress = point.join(ipAdresslist) self.clientProxy.userUpdateReceivedONE(self.userName, self.roomName) self.clientProxy.updateMovieAddressPort(self.roomName,ipAdress,port) self.clientProxy.joinRoomOKONE() self.flag=0 ####ACK leave system if(ack==1 and mestype==15): self.flag=1 self.nbtime=0 #self.timeout(data) self.obj.cancel() self.clientProxy.leaveSystemOKONE() self.location=-1 self.flag=0
def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport
class c2wUdpChatServerProtocol(DatagramProtocol): nbreOfHelloSent = {} UserToken = {} hostPortSequence = {} def __init__(self, serverProxy, lossPr, sequenceNumberSentHello=None): """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr self.SessionToken = 0 self.tokenSequenceList = [] self.SequenceNumber = 0 self.userID = 0 self.ACK = False self.c = 0 self.ce = 0 self.ACKofLRS = False self.sequenceNumberSentHello = sequenceNumberSentHello def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ global UserToken global hostPortSequence Packet = struct.unpack('>BBHHH' + str(len(datagram) - 8) + 's', datagram) Version = Packet[0] // (2**4) Type = Packet[0] - (2**4) * (Packet[0] // (2**4)) if Packet[3] > 0: self.SessionToken = c2wUdpChatServerProtocol.UserToken[host_port] else: self.SessionToken = Packet[1] * (2**16) + Packet[2] PayloadSize = Packet[4] Payload = Packet[5] self.SequenceNumber = Packet[3] if Type == 0: if self.sequenceNumberSentHello == c2wUdpChatServerProtocol.hostPortSequence[ host_port]: self.ACK = True c2wUdpChatServerProtocol.hostPortSequence[host_port] = Packet[3] if Type != 0: ## if the datagram isn't an ACK we have to send one to the client Ack = struct.pack( '>BBHHH', 16, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), c2wUdpChatServerProtocol.hostPortSequence[host_port], 0) self.transport.write(Ack, host_port) if Type == 1: decodedLoginRequest = self.decodeLoginRequest(Payload, host_port) ResponseCode, data = decodedLoginRequest[0], decodedLoginRequest[1] self.loginResponse(ResponseCode, data, host_port) if Type == 5: roomID = struct.unpack('>H', Payload)[0] user = self.serverProxy.getUserByAddress(host_port) print('my token', c2wUdpChatServerProtocol.UserToken[host_port]) for movie in self.serverProxy.getMovieList(): if movie.movieId == roomID: self.serverProxy.updateUserChatroom( user.userName, movie.movieTitle) self.serverProxy.startStreamingMovie(movie.movieTitle) if roomID == 1: self.serverProxy.stopStreamingMovie(user.userChatRoom) self.serverProxy.updateUserChatroom(user.userName, ROOM_IDS.MAIN_ROOM) self.sendToAll(Version) #self.tokenSequenceList.append(SessionToken) #self.tokenSequenceList.append(SequenceNumber) #Packet=struct.unpack('>BBHHH'+str(len(datagram)-8)+'s',datagram) #Version=Packet[0]//2**4 #Type=Packet[0]-Packet[0]//2**4 #SessionToken=Packet[1]*(2**16)+Packet[2] #SequenceNumber=Packet[3] #PayloadSize=Packet[4] #Payload=Packet[5] if Type == 7: username = self.serverProxy.getUserByAddress(host_port).userName self.serverProxy.removeUser(username) del c2wUdpChatServerProtocol.UserToken[host_port] self.sendToAll(Version) #self.sendToAll(Version) if Type == 3: self.sendToAll(Version) if Type == 6: sender = self.serverProxy.getUserByAddress(host_port) for user in self.serverProxy.getUserList(): if user.userAddress != host_port and sender.userChatRoom == user.userChatRoom: c2wUdpChatServerProtocol.hostPortSequence[ user.userAddress] += 1 sessionToken = c2wUdpChatServerProtocol.UserToken[ user.userAddress] Message = struct.pack( '>BBHHH' + str(len(Payload)) + 's', Version * 2**4 + Type, sessionToken // (2**16), sessionToken - (2**16) * (sessionToken // (2**16)), c2wUdpChatServerProtocol.hostPortSequence[ user.userAddress], len(Payload), Payload) self.transport.write(Message, user.userAddress) if c2wUdpChatServerProtocol.hostPortSequence[ host_port] >= 1 and self.serverProxy.getUserByAddress( host_port) != False: Hello = reactor.callLater(10, self.Hello, host_port) if self.c == 3: username = self.serverProxy.getUserByAddress( host_port).userName self.serverProxy.removeUser(username) self.sendToAll(Version) def decodeLoginRequest(self, Payload, host_port): uData = struct.unpack('>H' + str(len(Payload) - 2) + 's', Payload) uName = struct.unpack('H' + str(len(uData[1]) - 2) + 's', uData[1]) userName = uName[1].decode('utf-8') if self.SessionToken != 0: ResponseCode = 1 uData = struct.pack('>H' + str(len(uData[1])) + 's', 0, uData[1]) if len(userName) > 100: ResponseCode = 2 uData = struct.pack('>H' + str(len(uData[1])) + 's', 0, uData[1]) elif self.serverProxy.userExists(userName) == True: ResponseCode = 3 uData = struct.pack('>H' + str(len(uData[1])) + 's', 0, uData[1]) elif len(self.serverProxy.getUserList()) > 65535: ResponseCode = 4 uData = struct.pack('H' + str(len(uData[1])) + 's', 0, uData[1]) else: ResponseCode = 0 self.ce += 1 self.userID = self.serverProxy.addUser(userName, ROOM_IDS.MAIN_ROOM, None, host_port) self.SessionToken = random.getrandbits(24) c2wUdpChatServerProtocol.UserToken[host_port] = self.SessionToken uData = struct.pack('>H' + str(len(uData[1])) + 's', self.userID, uData[1]) return (ResponseCode, uData) def loginResponse(self, ResponseCode, uData, host_port): self.lastSequenceNumberSentLR = self.SequenceNumber Version = 1 Type = 2 Payload = struct.pack('>B' + str(len(uData)) + 's', ResponseCode, uData) LoginResponse = struct.pack( '>BBHHH' + str(len(Payload)) + 's', Version * 2**4 + Type, self.SessionToken // (2**16), self.SessionToken - (2**16) * (self.SessionToken // (2**16)), c2wUdpChatServerProtocol.hostPortSequence[host_port], len(Payload), Payload) self.transport.write(LoginResponse, host_port) #if ResponseCode!=0: if ResponseCode == 0: self.sendToAll(Version) def sendToAll(self, Version): Type = 4 RST = self.RST('Main Room') for user in self.serverProxy.getUserList(): c2wUdpChatServerProtocol.hostPortSequence[user.userAddress] += 1 sessionToken = c2wUdpChatServerProtocol.UserToken[user.userAddress] RSTall = struct.pack( '>BBHHH', Version * 2**4 + Type, sessionToken // (2**16), sessionToken - (2**16) * (sessionToken // (2**16)), c2wUdpChatServerProtocol.hostPortSequence[user.userAddress], len(RST)) + RST self.transport.write(RSTall, user.userAddress) def RST(self, Rname): if Rname == 'Main Room': RST1 = b"" RoomID = 1 MovieIP = 0 MoviePort = 0 MovieL = self.serverProxy.getMovieList() UsersList = [] for User in self.serverProxy.getUserList(): if User.userChatRoom == ROOM_IDS.MAIN_ROOM: UsersList.append( struct.pack('>HH' + str(len(User.userName)) + 's', User.userId, len(User.userName), User.userName.encode('utf-8'))) Userlist = struct.pack('>H', len(UsersList)) for user in UsersList: Userlist += user for movie in MovieL: RST1 += self.RST(movie.movieTitle) RST = struct.pack('>HH' + str(len(Rname)) + 'sIH', RoomID, len(Rname), Rname.encode('utf-8'), MovieIP, MoviePort) + Userlist + struct.pack( '>H', len(MovieL)) + RST1 return RST else: movie = self.serverProxy.getMovieByTitle(Rname) RoomId = movie.movieId MovieIP = movie.movieIpAddress MoviePort = movie.moviePort MovieL = [] RoomName = struct.pack('>H' + str(len(Rname)) + 's', len(Rname), Rname.encode('utf-8')) UsersList = [] for User in self.serverProxy.getUserList(): if User.userChatRoom == Rname or User.userChatRoom == ROOM_IDS.MOVIE_ROOM: UsersList.append( struct.pack('>HH' + str(len(User.userName)) + 's', User.userId, len(User.userName), User.userName.encode('utf-8'))) Userlist = struct.pack('>H', len(UsersList)) for user in UsersList: Userlist += user return struct.pack( '>H', RoomId) + RoomName + encodeIPadress(MovieIP) + struct.pack( '>H', MoviePort) + Userlist + struct.pack( '>H', len(MovieL)) def Hello(self, host_port): ##for user in self.serverProxy.getUserList(): #if user.userAddress==host_port: #username=user.userName if self.serverProxy.getUserByAddress(host_port) != False: c2wUdpChatServerProtocol.hostPortSequence[host_port] += 1 sessionToken = c2wUdpChatServerProtocol.UserToken[host_port] self.sequenceNumberSentHello = c2wUdpChatServerProtocol.hostPortSequence[ host_port] Version = 1 Type = 8 self.c += 1 Hello = struct.pack( '>BBHHH', Version * 2**4 + Type, self.SessionToken // (2**16), sessionToken - (2**16) * (sessionToken // (2**16)), c2wUdpChatServerProtocol.hostPortSequence[host_port], 0) self.transport.write(Hello, host_port) if self.c <= 3: if self.ACK == False: c2wUdpChatServerProtocol.hostPortSequence[ host_port] = c2wUdpChatServerProtocol.hostPortSequence[ host_port] - 1 reactor.callLater(10, self.Hello, host_port) #self.serverProxy.addUser(userName,'Main Room',None,host_port) #SequenceNumber+=1 #RoomIdentifier=1 #RoomName=struct.pack('>H9s',9,'Main Room'.encode('utf-8')) #MovieIP=0 #MoviePort=0 #UsersMainRoom=[] #for user in self.serverProxy.getUserList(): #if user.userChatRoom=='Main Room': #UsersMainRoom.append(struct.pack('>HH'+str(len(user.userName))+'s',user.userId,len(user.userName),userName.encode('utf-8'))) #NumberOfFilms=len(self.serverProxy.getUserList()) #for Movie in self.serverProxy.getUserList(): #RoomId=self.serverProxy.getMovieAddrPort(Movie) #RoomName=self.serverProxy. #length UsersMainRoom #Room List : number of films available , pour chaque film Room Identifier , Room Name , MovieIP , MoviePort pass
class c2wUdpChatServerProtocol(DatagramProtocol): def __init__(self, serverProxy, lossPr): """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr #{host_port: sequenceNumbersent,type} the server to the client self.usersequenceNumber = {} #{host_port : sesion} self.usersession = {} #{host_port : count} self.usercount = {} # #{useraddress, user name} # self.addresstouser={} #{host_port : flag} to store the sequence of message received self.userflag = {} #function callLater self.send = {} self.loginpacket = {} #to determine if the login is successful self.response = {} # 1- leave the systeme 0- non self.leave = {} # #{host_port : sequence received without ack} # self.receive={} #to store the room id self.roomID = [1] for movie in self.serverProxy.getMovieList(): self.roomID.append(movie.movieId) def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport # def generateSequenceNumber(self,userName): # """ # for every client # the sequence number incremented by one for each new packet # the sequence number after 65535 is 0 # """ # self.usersequenceNumber[userName][0] +=1 # self.usersequenceNumber[userName][0] = self.usersequenceNumber[userName][0] % 65535 # return self.usersequenceNumber[userName][0] def addr2dec(self, addr): """ Convert ip address to hexadecimal """ items = [int(x) for x in addr.split(".")] return sum([items[i] << [24, 16, 8, 0][i] for i in range(4)]) def ackResponse(self, datagram, host_port): """ When receiving a package from the server, respond to an ack to confirm the reception Here we need to unpack the received datagram, extract the sessionToken, Sequencenumber the payload here is a 0 """ version = 1 # the type of the ACK message is 0 typeMessage = 0 # Then we use the method called struct to finish the process of packing package and of unpacking package. # Specifilly, we use struct.pack and struct.unpack. # we have to divide the session(3 bits) into 2 parts of which the first part counts 2 bits, sessionToken_1, = struct.unpack('>H', datagram[1:3]) # and the second part counts 1 bit. sessionToken_2, = struct.unpack('>B', datagram[3:4]) sequenceNumber, = struct.unpack('>H', datagram[4:6]) # the payloadSize is null due to the specification payloadSize = 0 buf = struct.pack('>BHBHH', version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize) print('********Send ACK**************') print("ack :", buf) self.transport.write(buf, (host_port)) def sendChatMessageONE(self, datagram, host_port): """ The server now receives the message from the client. The next server will forward this message to all users in the same room. So we will now consider extracting the user's ID and then looking up his current room by ID. Traverse all users in this room and send this message to each user. """ # By checking the specification, we know that the userID's position inside the package is [8,10] # and messageLength is [10,12] userID, = struct.unpack('>H', datagram[8:10]) messageLength, = struct.unpack('>H', datagram[10:12]) message, = struct.unpack('>%ds' % messageLength, datagram[12:]) # Here we call the method which is implented by our prof, # it calls getUserList() who belongs to serverProxy. alluserList = self.serverProxy.getUserList() # Then we find all the users in the userList, we expect to find the userRoom by using the userName. for user in alluserList: if user.userId == userID: userRoom = user.userChatRoom userName = user.userName for user in alluserList: if user.userChatRoom == userRoom and user.userName != userName: version = 1 typeMessage = 6 sessionToken_1 = self.usersession[user.userAddress][0] sessionToken_2 = self.usersession[user.userAddress][1] # the userID(2 bits) and messageLength(2 bits), so here we add 4 bits with the messageLength. payloadSize = 4 + messageLength sequenceNumber = self.usersequenceNumber[user.userAddress][0] self.usersequenceNumber[user.userAddress][1] = 6 # Stop sending more than three times buf = struct.pack('>BHBHHHH%ds' % messageLength, version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize, userID, messageLength, message) # the limit times for transferring messages is 3 times. # if the we reach the limit, we suppose that this user is out of line.(we call removeUser()) if self.usercount[user.userAddress][0] == 3: print("Connection Lost") if self.leave[user.userAddress] == 0: self.serverProxy.removeUser(user.userName) self.leave[user.userAddress] = 1 for user in self.serverProxy.getUserList(): if user.userChatRoom == ROOM_IDS.MAIN_ROOM or user.userChatRoom == userRoom: self.roomState(user.userAddress) else: #send the packet print('*********Send Message**************') print("message :", buf) # print('***********************') self.transport.write(buf, user.userAddress) # Here we consider retransmitting this message after timing 1s without receiving ack response. # Stop sending more than three times # To realize this fonction, we call a method in reactor called callLater() self.usercount[user.userAddress][0] += 1 if self.usercount[user.userAddress][0] <= 3: self.send[user.userAddress] = [ reactor.callLater(1, self.sendChatMessageONE, datagram, host_port) ] def loginResponse(self, datagram, host_port): """ when receiving the login request sent by the client, the server sent the login response """ version = 1 typeMessage = 2 userLength, = struct.unpack('>H', datagram[10:12]) userName, = struct.unpack('>%ds' % userLength, datagram[12:12 + userLength]) userName = userName.decode() print(userName) # #to store the address of the client # self.addresstouser[host_port]=[userName] # if login successful: if self.usercount[host_port][0] != 0: buf = self.loginpacket[host_port][0] else: # Here we determine the loginRequest, we use responseCode (1 byte) to determine # The first case is Invalid User, which means that the user name has illegal characters. #Here we use regular expressions. Need to introduce re library. try: # we use the concept Regular Expression to solve this part if re.search(u'[^a-zA-Z0-9_.éèàçî]', userName): responseCode = 1 # The second case is that the username is too long (len(userName) > 100). elif userLength > 100: responseCode = 2 # The third situation is user name existed elif self.serverProxy.userExists(userName): responseCode = 3 # The fourth situation is Service not available elif len(self.serverProxy.getUserList()) > 65535: responseCode = 4 else: responseCode = 0 #Catching anomalies except userName: responseCode = 255 # if login successful: if responseCode == 0: userIdentifier = self.serverProxy.addUser( userName, ROOM_IDS.MAIN_ROOM, None, host_port) sequenceNumber = 0 #Randomly generated 24bits binaire sessionToken = random.getrandbits(24) print("sessionToken :", sessionToken) sessionToken_1 = sessionToken // 256 sessionToken_2 = sessionToken % 256 # Here we get a response code, so here we add 5 instead of 4 payloadSize = 5 + userLength buf = struct.pack('>BHBHHBHH%ds' % userLength, version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize, responseCode, userIdentifier, userLength, userName.encode('utf-8')) # if login is failed else: # sessionToken = 0 sessionToken = 0 sessionToken_1 = sessionToken // 256 sessionToken_2 = sessionToken % 256 sequenceNumber = 0 userIdentifier = 0 payloadSize = 5 + userLength buf = struct.pack('>BHBHHBHH%ds' % userLength, version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize, responseCode, userIdentifier, userLength, userName.encode('utf-8')) self.usersession[host_port] = [sessionToken_1, sessionToken_2] self.usersequenceNumber[host_port] = [0, 2] self.loginpacket[host_port] = [buf] self.response[host_port] = [responseCode] # Stop sending more than three times if self.usercount[host_port][0] == 3: print("Connection Lost") if self.leave[host_port] == 0 and self.response[host_port][0] == 0: self.serverProxy.removeUser(userName) self.leave[host_port] = 1 for user in self.serverProxy.getUserList(): if user.userChatRoom == ROOM_IDS.MAIN_ROOM: self.roomState(user.userAddress) else: print('********Send LoginResponse***************') print("loginresponse :", buf) # print('***********************') self.transport.write(buf, (host_port)) # Here we consider retransmitting this message after timing 1s without receiving ack response. # Stop sending more than three times self.usercount[host_port][0] += 1 if self.usercount[host_port][0] <= 3: self.send[host_port] = [ reactor.callLater(1, self.loginResponse, datagram, host_port) ] def messageMainroom(self): """ pack the data of the mainroom """ #Create a new user list in the main room userMainList = [] #Extract all users alluserList = self.serverProxy.getUserList() #Find the user in the main room in the list and add it to userMainList for user in alluserList: if user.userChatRoom == ROOM_IDS.MAIN_ROOM: userMainList.append((user.userName, user.userId)) #to pack the data of the mainroom roomIdentifier = 1 roomName = "Main Room" movieIP = 0 moviePort = 0 userNumber = len(userMainList) buf = struct.pack('>HH%dsIHH' % len(roomName.encode('utf-8')), roomIdentifier, len(roomName.encode('utf-8')), roomName.encode('utf-8'), movieIP, moviePort, userNumber) for i in range(userNumber): userName = userMainList[i][0] userLength = len(userName.encode('utf-8')) userIdentifier = userMainList[i][1] buf = buf + struct.pack('>HH%ds' % userLength, userIdentifier, userLength, userName.encode('utf-8')) #print("mainroom", buf) return buf def messageMovieroom(self, movieTitle): """ pack the data of the movie room """ #Create a new user list in the movie room userMovieList = [] #Extract all users alluserList = self.serverProxy.getUserList() #Extract all movies allmovieList = self.serverProxy.getMovieList() #to find the user in the movie room for user in alluserList: if user.userChatRoom == movieTitle: userMovieList.append((user.userName, user.userId)) #to pack the data in the movie room for movie in allmovieList: if movie.movieTitle == movieTitle: roomIdentifier = movie.movieId roomName = movieTitle movieIP = self.addr2dec(movie.movieIpAddress) #print("ip :", movieIP) moviePort = movie.moviePort userNumber = len(userMovieList) buf = struct.pack('>HH%dsIHH' % len(roomName.encode('utf-8')), roomIdentifier, len(roomName.encode('utf-8')), roomName.encode('utf-8'), movieIP, moviePort, userNumber) for i in range(userNumber): userName = userMovieList[i][0] userLength = len(userName.encode('utf-8')) userIdentifier = userMovieList[i][1] buf = buf + struct.pack('>HH%ds' % userLength, userIdentifier, userLength, userName.encode('utf-8')) roomList = 0 buf = buf + struct.pack('>H', roomList) #print("movieroom" , buf) return buf def roomState(self, host_port): """ to send the room state """ version = 1 typeMessage = 4 #to get the room of the client user = self.serverProxy.getUserByAddress(host_port) sessionToken_1 = self.usersession[host_port][0] sessionToken_2 = self.usersession[host_port][1] room = user.userChatRoom sequenceNumber = self.usersequenceNumber[host_port][0] self.usersequenceNumber[host_port][1] = 4 if room == ROOM_IDS.MAIN_ROOM: #if the client in the main room , call the function messageMainroom() mainbuf = self.messageMainroom() roomListLength = len(self.serverProxy.getMovieList()) #to get the data of the movie room roomListbuf = struct.pack('>H', roomListLength) for movie in self.serverProxy.getMovieList(): roomListbuf = roomListbuf + self.messageMovieroom( movie.movieTitle) payloadSize = len(mainbuf) + len(roomListbuf) #pack the header headbuf = struct.pack('>BHBHH', version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize) #the whole packet buf = headbuf + mainbuf + roomListbuf #if the client in the movie room else: #to call the function messageMovieroom(room) moviebuf = self.messageMovieroom(room) payloadSize = len(moviebuf) #the header headbuf = struct.pack('>BHBHH', version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize) #the whole packet buf = headbuf + moviebuf # Stop sending more than three times if self.usercount[host_port][0] == 3: print("Connection Lost") if self.leave[host_port] == 0: self.serverProxy.removeUser(user.userName) self.leave[host_port] = 1 for user in self.serverProxy.getUserList(): if (user.userChatRoom == ROOM_IDS.MAIN_ROOM or user.userChatRoom == room): self.roomState(user.userAddress) else: #send the packet print('********Send RoomState***************') print("roomState :", buf) # print('***********************') self.transport.write(buf, (host_port)) # Here we consider retransmitting this message after timing 1s without receiving ack response. # Stop sending more than three times self.usercount[host_port][0] += 1 if self.usercount[host_port][0] <= 3: self.send[host_port] = [ reactor.callLater(1, self.roomState, host_port) ] def hello(self, sequenceNumber, host_port): """ to check the client connection is still alive """ # print("***************************************") # print("now number",self.userflag[userName][0]) # print("past number",sequenceNumber) #the sequence number received no change if sequenceNumber == self.userflag[host_port][0]: #more than three consecutive HEL without receiving an ack ,remove the user if self.usercount[host_port][0] == 3: print("Connection Lost") if self.leave[host_port] == 0: user = self.serverProxy.getUserByAddress(host_port) room = user.userChatRoom self.serverProxy.removeUser(user.userName) self.leave[host_port] = 1 for user in self.serverProxy.getUserList(): if (user.userChatRoom == ROOM_IDS.MAIN_ROOM or user.userChatRoom == room): self.roomState(user.userAddress) else: #send the packet version = 1 typeMessage = 8 sessionToken_1 = self.usersession[host_port][0] sessionToken_2 = self.usersession[host_port][1] sequence = self.usersequenceNumber[host_port][0] self.usersequenceNumber[host_port][1] = 8 payloadSize = 0 buf = struct.pack('>BHBHH', version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequence, payloadSize) print('********Send Hello***************') print("hello :", buf) # print('***********************') self.transport.write(buf, (host_port)) # Here we consider retransmitting this message after timing 1s without receiving ack response. # Stop sending more than three times self.usercount[host_port][0] += 1 if self.usercount[host_port][0] <= 3: self.send[host_port] = [ reactor.callLater(1, self.hello, sequenceNumber, host_port) ] def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ # print(host_port) # Here we extract the first byte of the received data, including #the version and type, where we tend to take out the type. buf, = struct.unpack('>B', datagram[0:1]) typeMessage = buf % 16 (sessionToken_1, sessionToken_2, sequenceNumber) = struct.unpack('>HBH', datagram[1:6]) self.userflag[host_port] = [sequenceNumber] print('********Datagram Received**************') print("type :", typeMessage) print("sequence number", sequenceNumber) print("datagramReceived :", datagram) # print('***********************') # The first step we determine the type of information , if the type is ack, we need to # determine if the ack is received correctly, if the message is correctly,we cancel resend the packet if typeMessage == 0: if sequenceNumber == self.usersequenceNumber[host_port][ 0] and sessionToken_1 == self.usersession[host_port][ 0] and sessionToken_2 == self.usersession[host_port][1]: print("*******ACK Received******") self.send[host_port][0].cancel() self.usercount[host_port] = [0] self.usersequenceNumber[host_port][0] = ( self.usersequenceNumber[host_port][0] + 1) % 65535 if sequenceNumber == 0 and self.usersequenceNumber[host_port][ 1] == 2 and self.response[host_port][0] == 0: for user in self.serverProxy.getUserList(): if user.userChatRoom == ROOM_IDS.MAIN_ROOM: self.roomState(user.userAddress) #if the type is not an ack, we should respond the server to confirm the reception of the packet else: self.ackResponse(datagram, host_port) # typeMessage = 1 means request login;we call the function loginResponse if typeMessage == 1: print("login request") #the first sequncenumber is 0 self.leave[host_port] = 0 self.usercount[host_port] = [0] self.loginResponse(datagram, host_port) #typeMessage =5 :request join room ; elif typeMessage == 5 and sessionToken_1 == self.usersession[ host_port][0] and sessionToken_2 == self.usersession[ host_port][1]: print("join room") usernow = self.serverProxy.getUserByAddress(host_port) roomId, = struct.unpack('>H', datagram[8:10]) # room id is wrong if roomId not in self.roomID: self.roomState(host_port) else: #the client joins to main room if roomId == 1: self.serverProxy.stopStreamingMovie( usernow.userChatRoom) userPastroom = usernow.userChatRoom self.serverProxy.updateUserChatroom( usernow.userName, ROOM_IDS.MAIN_ROOM) for user in self.serverProxy.getUserList(): if (user.userChatRoom == ROOM_IDS.MAIN_ROOM or user.userChatRoom == userPastroom): self.roomState(user.userAddress) #the client joins to movie room else: for movie in self.serverProxy.getMovieList(): if movie.movieId == roomId: self.serverProxy.updateUserChatroom( usernow.userName, movie.movieTitle) self.serverProxy.startStreamingMovie( movie.movieTitle) for user in self.serverProxy.getUserList(): if (user.userChatRoom == movie.movieTitle or user.userChatRoom == ROOM_IDS.MAIN_ROOM): self.roomState(user.userAddress) #typeMessage =7 : leave system elif typeMessage == 7 and sessionToken_1 == self.usersession[ host_port][0] and sessionToken_2 == self.usersession[ host_port][1]: print("leave system") usernow = self.serverProxy.getUserByAddress(host_port) self.serverProxy.removeUser(usernow.userName) self.leave[host_port] = 1 for user in self.serverProxy.getUserList(): if user.userChatRoom == ROOM_IDS.MAIN_ROOM: self.roomState(user.userAddress) #typeMessage =7 : send message elif typeMessage == 6 and sessionToken_1 == self.usersession[ host_port][0] and sessionToken_2 == self.usersession[ host_port][1]: print("chat message") self.sendChatMessageONE(datagram, host_port) # #sequence number received add 1 # self.receive[host_port] = (self.receive[host_port]+1) % 65535 if self.leave[host_port] == 0 and self.response[host_port][0] == 0: #call the function hello , if no message receiving during 10s reactor.callLater(10, self.hello, sequenceNumber, host_port)
class c2wUdpChatServerProtocol(DatagramProtocol): def __init__(self, serverProxy, lossPr): """ :param serverProxy: The serverProxy, which the protocol must use to interact with the user and movie store (i.e., the list of users and movies) in the server. :param lossPr: The packet loss probability for outgoing packets. Do not modify this value! Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attribute: .. attribute:: serverProxy The serverProxy, which the protocol must use to interact with the user and movie store in the server. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The serverProxy, which the protocol must use #: to interact with the server (to access the movie list and to #: access and modify the user list). self.serverProxy = serverProxy self.lossPr = lossPr self.tailleHeader=4 self.listUser={} def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendAck(self, message, user): #envoyer un message d'acquittement """ Takes in parameter: :param string message: the message received :param string user: param user user: instance of the receiver user Used to send an acknolegment message when a message is received from a client """ ack={} ack["taille"]=self.tailleHeader ack["Type"]=63 numberSeq = message.get("seq") if (numberSeq == user.userSeq): ack["seq"]=message.get("seq") user.incrementeUserSeq() else: ack["seq"]=user.userSeq-1 send=encoder(ack) print("E : {0}".format(ack)) self.transport.write(send, (user.host, user.port)) def addUser(self, name, message, user): #ajouter un nouvel utilisateur à la liste des utilisateurs utilisant l'application (cote serveur) """ Takes in parameters : :param string name: the username of the new user :param string message: the message received :param user user: instance of the concerned user Used to add the user to the server. 1) Username check : - not already used - less than 124 caracter - does not contain spaces 2) Add the new user to the user list on the server in the MAIN ROOM """ if (self.serverProxy.getUserByName(name)): #verifier si le nom d'utilisateur est dejà utilise ou non sur la liste dico={} dico["taille"]=self.tailleHeader+1 dico["Type"]=8 dico["erreur"]=1 return dico #envoyer un message avec une erreur "1" if (len(name.encode('utf-8'))>124): #verifier si le nom d'utilisateur ne depasse pas 124 caractères dico={} dico["taille"]=self.tailleHeader+1 dico["Type"]=8 dico["erreur"]=2 #envoyer un message avec une erreur "2" return dico if (" " in name): #verifier si le nom d'utilisateur contient des espaces dico={} dico["taille"]=self.tailleHeader+1 dico["Type"]=8 dico["erreur"]=3 #envoyer un message avec une erreur "3" return dico self.serverProxy.addUser(message.get("user"), ROOM_IDS.MAIN_ROOM, None,(user.host,user.port)) #si aucune des conditions n'est respectee, c'est que le nom peut etre utilise, on ajoute alors l'utilisateur à la liste dico={} dico["taille"]=self.tailleHeader dico["Type"]=7 return dico def sendMessageToUserChat(self, message, user): """ Takes in parameters : :param dict message: the received message :param user user: instance of the concerned user Adds the message in the wainting list of the receiver. If this list contains only ONE message, the sendMessageToClient function if launched. """ message["seq"]=user.serverSeq user.addMessage(message) if(len(user.message)==1): self.sendMessageToClient(user) def sendMessageToClient(self, user): """ Takes in parameters : :param user user: instance of the concerned user Sends the message to the concerned client and launchs the sendAndWait function """ message=user.message[0] message["seq"]=user.serverSeq send=encoder(message) print("E nom {0} : {1}".format(user.name,message)) self.transport.write(send, (user.host,user.port)) user.idSend=reactor.callLater(1, self.sendAndWait, user) def sendAndWait(self, user): """ Takes in parameter : :param user user: instance of the concerned user Sets the SendAndWait functionality : it sends the message 10 times, every seconds """ if(user.numberTransmission<10): send=encoder(user.message[0]) print("E nom {0} : {1}".format(user.name,user.message[0])) #print("E : {0}".format(user.message[0])) self.transport.write(send, (user.host, user.port)) user.numberTransmission+=1 user.idSend=reactor.callLater(1, self.sendAndWait, user) if(user.numberTransmission==10): print("salut : {0}".format(user.name)) user.idSend.cancel() self.deleteUser(user) def sendMovies(self, message): """ Takes in parameter: :param dict message: the received message Function used to get the movies availables on the server and to send them in a list to the newly connected client """ movies=self.serverProxy.getMovieList() #recuperer la liste des films aupres du serveur films={} #creatrion du dictionnaire à RENVOYER films["seq"]=message.get("seq")+1 films["Type"]=2 i=0 taille=0 while(i<len(movies)): films["ip{0}".format(i)]=movies[i].movieIpAddress #sur 4 octets films["port{0}".format(i)]=movies[i].moviePort #sur 1 octet films["Id{0}".format(i)]=movies[i].movieId #sur 1 octet films["nom{0}".format(i)]=movies[i].movieTitle #taille variable films["taille{0}".format(i)]=8+len(movies[i].movieTitle) #inclu ip, port, id, nom et lui meme sur 2 octets taille+=films.get("taille{0}".format(i)) i+=1 films["taille"]=self.tailleHeader+taille return films def sendUsers(self,message): """ Takes in parameter: :param dict message: the received message Function used to get the users connected on the server and to send them in a list to the newly connected client """ users=self.serverProxy.getUserList()#recuper la liste des utilisateurs utilisateurs={} #creatrion du dictionnaire à RENVOYER utilisateurs["seq"]=message.get("seq")+2 utilisateurs["Type"]=3 i=0 taille=0 while(i<len(users)): #tant que l'on arrive pas a la fin de la liste des utilisateurs utilisateurs["user{0}".format(i)]=users[i].userName #taille variable if(users[i].userChatRoom==ROOM_IDS.MAIN_ROOM): #si l'utilisateur est dans la main room ... utilisateurs["Id{0}".format(i)]=0 #l'id a envoyer est 0 else:#sinon ... room=users[i].userChatRoom #on recupere l'id de la room dans lequel il se trouve print("roooooooooooooooooooom : {0}".format(room)) movie=self.serverProxy.getMovieById(room) utilisateurs["Id{0}".format(i)]=movie.movieId #sur 1 octet utilisateurs["taille{0}".format(i)]=2+len(users[i].userName.encode('utf-8')) taille+=utilisateurs.get("taille{0}".format(i)) i+=1 utilisateurs["taille"]=self.tailleHeader+taille return utilisateurs def updateUser(self,user,roomId): """ Takes in parameter: :param user user: instance of the concerned user :param int roomId : the new room's id where the client is now connected Used to update the status of a user : in which room he is or if he has left the application This message is sent to all the users connected """ dicoUpdate={} userName=user.name #recuperer l'utisateur dont on doit envoyer le nouveau statut dicoUpdate["seq"]=0 dicoUpdate["Type"]=4 dicoUpdate["taille"]=4+1+len(userName.encode('utf-8')) dicoUpdate["user"]=userName if(roomId==ROOM_IDS.MAIN_ROOM): #si l'utilisateur est dans la main room dicoUpdate["Id"]=0 # l'id vaut 0 else: #sinon... dicoUpdate["Id"]=roomId #il vaut l'id passé en parametre allUsers=self.serverProxy.getUserList() #recuperation de la liste des utilisateurs connectes for u in allUsers: #pour chacun des utilisateurs de la liste: receiver=self.listUser.get("{0}".format(u.userAddress)) #on recupere l'instrance TCP pour cet utilisateur self.sendMessageToUserChat(dicoUpdate, receiver) #on envoie le dictionnaire a chaque client def messageToForward(self,message,user): """ Takes in parameter: :param dict message: the received message :param user user: instance of the concerned user Used to prepare the message which will be forwarded e.g : chat message """ dicoToSend={} dicoToSend["Type"]=10 dicoToSend["size"]=len(user.name.encode('utf-8')) dicoToSend["user"]=user.name dicoToSend["message"]=message dicoToSend["taille"]=self.tailleHeader+1+len(user.name.encode('utf-8'))+len(message.encode('utf-8')) return dicoToSend def gestionStream(self, movieId): """ Takes in parameter: :param int movieId : the id of the movie which have to be started Used to start the streaming of a movie """ self.serverProxy.startStreamingMovie(self.serverProxy.getMovieById(movieId).movieTitle) def deleteUser(self, user): """ Takes in parameter: :param user user: instance of the concerned user Used to delete a user on the server when this one has totally left the application """ if(self.serverProxy.getUserByAddress((user.host,user.port))): self.serverProxy.removeUser(user.name) self.updateUser(user,255) #id donné lorsqu'un utilisateur quitte la plateforme del self.listUser["{}".format((user.host,user.port))] else: del self.listUser["{}".format((user.host,user.port))] print(self.listUser) def receiverReady(self, user): """ Takes in parameter: :param user user: instance of the concerned user Used to increment the client's sequence number on the server. It means that the client is ready to receive another message from the server """ if(user.error==0): #si le message de connexion ne contient pas d'erreur user.idSend.cancel() #on arrete le S&W user.delMessage() #on supprime le message de la liste des message a envoyer a l'utilisateur user.incrementeServerSeq() #on incremente le numero de sequence de cet utilisateur sur le serveur user.numberTransmission=1 #on réinitialise le numbre de transmissions if(len(user.message)>0): #si la la liste des messages a envoyer au client n'est pas vide self.sendMessageToClient(user) #on envoie le message suivant else: #sinon print("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") print(user.idSend) user.idSend.cancel() #on arrete le S&W self.deleteUser(user) #on supprime l'utilisateur def forwardMessage(self, message, user): """ Takes in parameter: :param dict message: the received message :param user user: instance of the concerned user Used to send the message which will be forwarded e.g : chat message """ idMovieSender=self.serverProxy.getUserByName(user.name).userChatRoom#recherche de l'id de la room dans laquelle se trouve l'utilisateur usersList=self.serverProxy.getUserList() #on recupere la liste des utilisateurs connectes au serveur receivers=[] #liste contenant les destinataires i=0 while(i<len(usersList)): #tant que l'on arrive pas au bout de la liste des utilisateurs if(usersList[i].userChatRoom==idMovieSender): #si le destinataire est dans la meme room que l'emetteur receivers.append(usersList[i]) #on l'ajoute dans la liste des destinataires i+=1 for u in receivers: #pour chaque destinataire receiver=self.listUser.get("{0}".format(u.userAddress)) #on recupere l'instrance TCP pour cet utilisateur self.sendMessageToUserChat(message, receiver)#on envoie le dictionnaire a chaque client def joinOKRoom(self, message): """ Takes in parameter: :param dict message: the received message Used to prepare the message which is sent to accept that a user has changed of room """ dicoToSend={} dicoToSend["Type"]=11 dicoToSend["taille"]=4 return dicoToSend def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Twisted calls this method when the server has received a UDP packet. You cannot change the signature of this method. """ message = decoder(datagram) #on decode le message reçu print("\nR : {0}".format(message)) if(self.listUser.get("{}".format(host_port))==None): #si l'utilisateur n'existe pas self.listUser["{}".format(host_port)]=userChat(message.get("user"),host_port) #on le crée user = self.listUser.get("{0}".format(host_port)) Type = message.get("Type") Seq = message.get("seq") if Type == 63: #si le message est un acquittement if (Seq == user.serverSeq): #si le num de seq de l'ack est celui enregsitre sur le server self.receiverReady(user) else:#si ce n'est pas un ack if Seq < user.userSeq: #si le num de seq recu est inferieur a celui du client enregsitre sur le serveur self.sendAck(message, user) #on envoe un ack (=deja traite) else: #sinon self.sendAck(message, user) #on envoie un ack avant traitement if Type==1: #si le message est une inscription print(message.get("user")) testAdding=self.addUser(message.get("user"), message, user)#on essaye d'ajouter l'utilisateur sur le serveur if (len(testAdding) > 2): # si le retour contient un champ erreur en plus de l'entete user.error=1 #on place la variable error a 1 pour signifier qu'une erreur est survenue self.sendMessageToUserChat(testAdding, user) #on le place dans la liste des message a envoyer a l'utilisateur else: #s'il n'y a pas d'erreur self.sendMessageToUserChat(testAdding, user)#on le place dans la liste des message a envoyer a l'utilisateur movies=self.sendMovies(message)#recupere la liste des films self.sendMessageToUserChat(movies, user)#on la place dans la liste des message a envoyer a l'utilisateur users=self.sendUsers(message)#on recupere la liste des utilisateurs self.sendMessageToUserChat(users, user)#on la place dans la liste des message a envoyer a l'utilisateur self.updateUser(user,ROOM_IDS.MAIN_ROOM) #on actualise l'emplacement de l'utilisateur (il se trouve dans la main room) if Type==5: #si le message est un message de chat messageToForward=self.messageToForward(message.get("message"), user)#on prepare le message a transferer self.forwardMessage(messageToForward, user)#on transfere le message a tous les utilisateurs presents dans la main room if Type==6: #si le message est une requete pour changer de room okRoom = self.joinOKRoom(message)#on prepare a envoyer le message d'aceptation self.sendMessageToUserChat(okRoom, user) #on le place dans la liste des message à envoyer à l'utilisateur movieId=message.get("Id")#on recupere l'id de la nouvelle room if movieId==0: #s'il vaut 0 self.serverProxy.updateUserChatroom(user.name, ROOM_IDS.MAIN_ROOM)#on actualise le statut de l'utilisateur qui se trouve dans la main room else: #sinon self.serverProxy.updateUserChatroom(user.name, movieId) #on actualise le statut de l'utilisateur qui se trouve dans une movie room self.updateUser(user,movieId) #on actualise le statut if Type==9: #si le message est une requete de deconnexion self.deleteUser(user) self.deleteUser() #on supprime l'utilisateur du serveur pass
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.seq = 1 self.tailleHeader = 4 self.dicoFilm = { } # dictionnaire {Id:titreFilm,.....} ex:{"5":"Big Bunny",....} #Useful for the programming of the send & wait policy self.compteurSAW = 0 self.Ackrecieved = False self.user = "" # le nom d utilisateur que le client va utiliser: ca ne changera pas def incrementeSeq(self): if (self.seq < 1023): self.seq += 1 else: self.seq = 1 def writingMessage(self, Liste): """ Will be used for the function in the reactors. The reactor function callLater() has 3 arguments. Delay, the function to call and its argument It is thus easier to use function with only one argument for callLater """ [message, (adresse, port)] = Liste self.transport.write(message, (adresse, port)) def sendAndWait(self, message): #on initialise la variable qui va être appelée par la fonction callLater ListeArg = [message, (self.serverAddress, self.serverPort)] #on initialise les deux callLater qui vont être utilisées au cas où callServer = reactor.callLater(1, self.writingMessage, ListeArg) message = ListeArg[0] RerunFunction = reactor.callLater(1, self.sendAndWait, message) if self.compteurSAW == 10: #notifier le serveur avec un message d'erreur print( "warning, didn't recieved any message of the following type:", str(63)) else: if self.AckRecieved == False: #si aucun message de type 63 n'est reçu, on en renvoie un self.compteurSAW += 1 print("compteurSAW =", self.compteurSAW, "at time:", time.time()) reactor.run() else: #une fois que cela s'est fait, on arrete les envois en boucles. callServer.cancel() RerunFunction.cancel() #on réinitialise l'attribut AckRecieved self.AckRecieved = False pass def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): #""" #:param string userName: The user name that the user has typed. #The client proxy calls this function when the user clicks on #the login button. #""" moduleLogger.debug('loginRequest called with username=%s', userName) dico = {} dico["taille"] = self.tailleHeader + len(userName) dico["Type"] = 1 dico["seq"] = self.seq dico["user"] = userName self.user = userName connexionRq = encoder(dico) self.transport.write(connexionRq, (self.serverAddress, self.serverPort)) #sendAndWait(connexionRq) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ #on prépare le message pour l'envoie au serveur dicoMessage = {} dicoMessage["taille"] = self.tailleHeader + len(message) dicoMessage["Type"] = 5 dicoMessage["seq"] = self.seq dicoMessage["message"] = message msgchat = encoder(dicoMessage) self.transport.write(msgchat, (self.serverAddress, self.serverPort)) #on applique la politique de send-and-wait self.sendAndWait(msgchat) #une fois que le serveur a acquitté, on attends la liste de film pass def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ #on prépare le message pour l'envoie au serveur #print("roomname={0}".format(roomName)) dicoRequete = {} dicoRequete["taille"] = self.tailleHeader + 1 dicoRequete["Type"] = 6 dicoRequete["seq"] = self.seq if roomName == ROOM_IDS.MAIN_ROOM: dicoRequete["Id"] = 0 else: for Id, nom in self.dicoFilm.items(): if nom == roomName: dicoRequete["Id"] = int(Id) joindreRq = encoder(dicoRequete) #sendAndWait(joindreRq) self.transport.write(joindreRq, (self.serverAddress, self.serverPort)) pass def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ logoutDico = {} logoutDico["taille"] = self.tailleHeader logoutDico["Type"] = 9 logoutDico["sequence"] = self.seq paquet = encoder(logoutDico) self.transport.write(paquet, (self.serverAddress, self.serverPort)) pass def sendAcknowledgeOIE( self, ackSeq ): #ackSeq est la sequence contenue dans le message à acquitter """ Send an aknowledgment message """ ackDico = {} ackDico["taille"] = self.tailleHeader ackDico["Type"] = 63 ackDico["seq"] = ackSeq ackDatagram = encoder(ackDico) self.transport.write(ackDatagram, (self.serverAddress, self.serverPort)) def datagramReceived(self, datagram, host_port): # a finir """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ messageDico = decoder(datagram) Type = messageDico.get("Type") ##if Type!=63: #il faut acquitter tous les paquets sauf un acquittement if Type == 2: #le datagram contient la liste des films self.movieList = [] i = 0 while "taille{0}".format(i) in messageDico: #print(messageDico) tupleTemporaire = (str(messageDico["nom{}".format(i)]), str(messageDico["ip{}".format(i)]), str(messageDico["port{}".format(i)])) self.movieList.append(tupleTemporaire) self.dicoFilm["{0}".format(messageDico["Id{}".format( i)])] = messageDico["nom{0}".format(i)] print(self.dicoFilm) i += 1 ackSeq = messageDico["seq"] self.sendAcknowledgeOIE(ackSeq) if Type == 3: #Le datagram contient la liste des utilisateurs self.userList = [(self.user, ROOM_IDS.MAIN_ROOM)] i = 0 while "taille{0}".format(i) in messageDico: if messageDico["Id{0}".format(i)] == 0: roomName = ROOM_IDS.MAIN_ROOM else: roomId = messageDico["Id{0}".format( i)] #roomId est un str contenant un decimal: ex "5" roomName = self.dicoFilm["{0}".format(roomId)] tupleTemporaire = (str(messageDico["user{0}".format(i)]), roomName) print("tupletemporaire={0}".format(tupleTemporaire)) self.userList.append(tupleTemporaire) i += 1 #print(self.userList) #print(self.movieList) ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) self.clientProxy.initCompleteONE(self.userList, self.movieList) if Type == 4: # le datagram est une MAJ utilisateur #print("entree type 4") roomId = messageDico["Id"] userName = messageDico["user"] if roomId == 0: roomName = ROOM_IDS.MAIN_ROOM else: #chercher le titre du Film associé à cet Id (et pas le nom générique ROOM_IDS.MOVIE_ROOM) for Id, nom in self.dicoFilm.items(): if int(Id) == roomId: roomName = nom #roomName=ROOM_IDS.MOVIE_ROOM for Tuple in self.userList: if Tuple[0] == userName: self.userList[self.userList.index(Tuple)][1] = roomName #self.setUserListONE(self.userList) self.clientProxy.userUpdateReceivedONE(userName, roomName) if Type == 7: # inscription acceptee #self.clientProxy.joinRoomOKONE() #charger l interface graphique #ackSeq=messageDico["seq"] #pour l'acquittement du datagram #self.sendAcknowledgeOIE(ackSeq) pass if Type == 8: # error: inscription refusee errorCode = messageDico["erreur"] if errorCode == 1: self.clientProxy.connectionRejectedONE( "nom d'utilisateur déjà utilisé") elif errorCode == 2: self.clientProxy.connectionRejectedONE( "nom d'utilisateur dépassant 254 octets") elif errorCode == 3: self.clientProxy.connectionRejectedONE( "nom d'utilisateur contenant un ou plusieurs espaces") if Type == 10: #recevoir un message de chat des autres utilisateurs if not messageDico["user"] == self.user: rUserName = messageDico["user"] message = messageDico["message"] self.clientProxy.chatMessageReceivedONE(rUserName, message) ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) if Type == 11: # ack: connexion a une salle réussie self.clientProxy.joinRoomOKONE() if Type == 12: # ack: connexion a une salle echouee ou rejetee ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) self.clientProxy.connectionRejectedONE( "Echec: veuillez vous reconnecter" ) #message erreur + reouverture de l interface pour une nouvelle tentative if Type == 63: self.AckRecieved = True #on incrémente le numéro de séquence self.incrementeSeq() else: ackSeq = messageDico["seq"] #pour l'acquittement du datagram self.sendAcknowledgeOIE(ackSeq) return messageDico
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.movie_list=[] self.user_list = [] self.num_seq=0 self.expected_num_seq=0 self.user_name="" self.timer=None self.room='' self.booleanDict={} #dictionnaire de boolean self.received_packet_history={} self.sent_packet_history={} self.queuepacket=[] self.sendAndWait=True def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) packet=Packet(0,0,len(userName)+1,[len(userName),userName]) self.sendPacket(packet,(self.serverAddress, self.serverPort)) #dés que j'envoie un data j'incremente mon num seq self.user_name=userName def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ # print(printable) # message=''.join(filter(lambda x: x in printable, message)) # print(message) seqNumber=self.num_seq message_packet = Packet(64, seqNumber, len(self.user_name) +len(message)+2, [len(self.user_name), self.user_name.encode('utf-8'),len(message),message.encode('utf8')]) message_packet.packet_format='!LB' + str(message_packet.data[0]) + 's' + 'B' + str(message_packet.data[2]) + 's' self.sendPacket(message_packet,(self.serverAddress, self.serverPort)) def ack(self,packet,host_port): ack=Packet(80,packet.numSeq,0,None) self.transport.write(ack.pack(), host_port) def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ self.room_wanted=roomName #disconnection from a movie room to the main room if roomName==ROOM_IDS.MAIN_ROOM: main_packet=Packet(49,self.num_seq,0,None) self.sendPacket(main_packet,(self.serverAddress, self.serverPort)) # connection to a movie room: else: movie_packet=Packet(48,self.num_seq,len(roomName)+1,[len(roomName),roomName]) self.sendPacket(movie_packet,(self.serverAddress, self.serverPort)) def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ leave_paquet=Packet(3,self.num_seq,0,"") self.sendPacket(leave_paquet, (self.serverAddress, self.serverPort)) def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ packet = Packet(0, 0, 0, 0) packet.unpackHeader(datagram) print("CLIENT RECEIVE A PACKET",packet) #if we receive a message we ack it if packet.message_type != 80 : self.ack(packet,host_port) #here we have a verified num_seq and and ack message we should cancel our callater if (packet.message_type ==80): if self.num_seq==packet.num_seq: self.num_seq = (self.num_seq + 1) % 1023 if self.timer is not None: print("TIMER CANCEL") self.timer.cancel() self.timer=None self.sendAndWait = True self.sendqueuepacket() if self.lastPacketSent().message_type in [48,49]: #if we receive the ack of join room or leave room self.room=self.room_wanted self.clientProxy.joinRoomOKONE() elif self.lastPacketSent().message_type==3: #if we receive the ack of leave main room self.clientProxy.leaveSystemOKONE() else: return 0 elif packet.num_seq in self.received_packet_history.keys() and self.received_packet_history[packet.num_seq].isEqual(packet): print("paquet ignoré",packet,self.received_packet_history[packet.num_seq]) elif self.expected_num_seq != packet.num_seq: #todo self.wrongExpectedNumSeq(host_port) #dans ce stade on est sur que le num seq est verifié et quil sagit pas dun message d'ack elif self.sendAndWait: print('') self.received_packet_history[packet.num_seq] = packet self.expected_num_seq = (self.expected_num_seq+1)%1023 if packet.message_type ==1: print('connexion etablie') elif packet.message_type ==2: print('connexion echouée') elif packet.message_type==128: self.errorReceived(datagram) elif packet.message_type==16: self.movieListReceived(packet,datagram) elif packet.message_type==19: self.updateUserListMainRoom(packet,datagram) elif packet.message_type ==65: self.chatReceived(packet,datagram) elif packet.message_type ==18: self.updateUserListMovieRoom(packet,datagram) def chatReceived(self,packet,datagram): packet.data=[0,0,0,0] # offset=4 # lenSender=struct.unpack_from('!B',datagram,offset)[0] # offset+=1 # sender=struct.unpack_from('!'+str(lenSender)+'s',offset)[0] # offset+=lenSender # lenMessage=struct.unpack_from('!B',datagram,offset)[0] # message=struct.unpack_from('!'+str(lenMessage)+'s') len_sender = struct.unpack_from('!B', datagram, offset=4)[0] format = '!LB' + str(len_sender) + 'sB' + str(packet.packet_length - 2 - len_sender) + 's' headerDec, packet.data[0], packet.data[1], packet.data[2], packet.data[3] = struct.unpack(format, datagram) if packet.data[1].decode('utf-8') != self.user_name: self.clientProxy.chatMessageReceivedONE(packet.data[1].decode('utf8'),packet.data[3].decode('utf8')) def wrongExpectedNumSeq(self,host_port): self.error('wrongNumSeq:'+str(self.expected_num_seq), host_port) def error(self,errorType,host_port): error=Packet(128,self.num_seq,len(errorType)+1,[len(errorType),errorType]) self.sendPacket(error, host_port) def errorReceived(self,datagram): packet = Packet(0, 0, 0, 0) packet.unpackHeader(datagram) format = '!LB' + str(packet.packet_length - 1) + 's' packet.data = [0, 0] headerDec, packet.data[0], packet.data[1] = struct.unpack(format, datagram) if packet.data[1].decode('utf8') in ["wrongUserName","serverSaturated","userExists"]: self.clientProxy.connectionRejectedONE(packet.data[1].decode("utf8")) elif 'wrongNumSeq:' in packet.data[1].decode("utf8"): self.num_seq=int(packet.data[1][12:].decode("utf8")) print("error :",packet.data[1].decode("utf8")) return 0 def updateUserListMainRoom(self,packet,datagram): #here we receive the new list of user in main room self.user_list=[] packet.data=[] offset=4 len_Rooms = struct.unpack_from("!H", datagram, offset)[0] # number of movie room i=0 offset += 2 packet.data.append(len_Rooms) for i in range(len_Rooms): len_UserList = struct.unpack_from('!H', datagram, offset=offset)[0] packet.data.append(len_UserList) offset+=2 if len_UserList!=0: #if room is not empty for j in range(0,len_UserList): len_UserName = struct.unpack_from('!B', datagram, offset=offset)[0] packet.data.append(len_UserName) offset+=1 user=struct.unpack_from( "!" + str(len_UserName) + "s", datagram, offset=offset)[0] packet.data.append(user.decode('utf8')) offset+=len_UserName if i==0: movie_title=ROOM_IDS.MAIN_ROOM elif self.movie_list ==[]: movie_title=i else: movie_title=self.movie_list[i-1] user_tuple=(user.decode('utf8'),movie_title) #("alice","batman") self.user_list.append(user_tuple) if "initok" in self.booleanDict and self.booleanDict["initok"] == True: print("set user ok") self.clientProxy.setUserListONE(self.user_list) else: pass def updateUserListMovieRoom(self, packet, datagram): self.user_list = [] packet.data = [] offset = 4 len_users = struct.unpack_from("!H", datagram, offset)[0] # nombre de users offset += 2 packet.data.append(len_users) for j in range(0, len_users): len_UserName = struct.unpack_from('!B', datagram, offset=offset)[0] packet.data.append(len_UserName) offset += 1 user = struct.unpack_from("!" + str(len_UserName) + "s", datagram, offset=offset)[0] packet.data.append(user.decode('utf8')) offset += len_UserName user_tuple = (user.decode('utf8'), self.room) self.user_list.append(user_tuple) self.clientProxy.setUserListONE(self.user_list) def movieListReceived(self,packet,datagram): self.movie_list=[] packet.data = [] len_movie_list=struct.unpack_from("!H",datagram,offset=4)[0] #nombre de salon packet.data.append(len_movie_list) offset = 6 i=0 while i< len_movie_list: format = "!B" len_movie_title=struct.unpack_from(format,datagram,offset=offset)[0] packet.data.append(len_movie_title) format="!"+str(len_movie_title)+"s" offset+=1 movie_title=struct.unpack_from(format,datagram,offset=offset)[0] packet.data.append(movie_title.decode("utf8")) ip_movie=[] format = "!B" offset+= len_movie_title #unpacker l'adresse ip for j in range(0,4): ip_movie.append(struct.unpack_from(format,datagram,offset=offset)[0]) offset += 1 ip_movie=list(map(str,ip_movie)) #ecrire dans une liste les 4 octet de l'adresse ip packet.data.append('.'.join(list(map(str,ip_movie)))) format='!H' port_movie=struct.unpack_from(format,datagram,offset)[0] packet.data.append(port_movie) offset+=2 i=i+1 movie_tuple=(movie_title.decode("utf8"),ip_movie,port_movie) self.movie_list.append(movie_tuple) user_list_update=[] for user in self.user_list: if user[1]!=ROOM_IDS.MAIN_ROOM: movie_title=self.movie_list[user[1]-1] else: movie_title=ROOM_IDS.MAIN_ROOM user_list_update.append((user[0],movie_title)) self.user_list=user_list_update self.clientProxy.initCompleteONE(self.user_list, self.movie_list) self.booleanDict['initok'] = True def sendPacket(self,packet,host_port,call_count=1):#this function assure the SendAndWait protocol if self.sendAndWait == True and call_count==1: print('CLIENT SEND PACKET', packet) self.sent_packet_history[packet.num_seq] = packet self.sendAndWait=False self.transport.write(packet.pack(), host_port) call_count+=1 if call_count <= 4: #pass self.timer=reactor.callLater(5, self.sendPacket,packet, host_port, call_count) elif call_count>1: print('CLIENT SEND PACKET', packet) self.transport.write(packet.pack(), host_port) call_count += 1 if call_count <= 4: # pass self.timer = reactor.callLater(5, self.sendPacket, packet, host_port, call_count) else: self.queuepacket.append((packet,host_port)) def sendqueuepacket(self): print("SEND AND WAIIIIIT") for packethost in self.queuepacket: self.sendPacket(*packethost) self.queuepacket=[] def ack(self,packet,host_port): ack=Packet(80,packet.num_seq,0,None) self.transport.write(ack.pack(), host_port) def lastPacketReceived(self): return self.received_packet_history[len(self.received_packet_history.keys())-1] def nlastPacketReceived(self,i): return self.received_packet_history[len(self.received_packet_history.keys())-i] def lastPacketSent(self): return self.sent_packet_history[len(self.sent_packet_history.keys())-1] def nlastPacketSent(self,i): return self.sent_packet_history[len(self.sent_packet_history.keys())-i]
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr #: the serial number of the packet send by the client self.sequenceNumber = 0 # : the instance of a client-server connection self.sessionToken_1 = 0 self.sessionToken_2 = 0 #: the name of the client self.userName = '' #: the identifier of the client self.userIdentifier = 0 #:the state of the client 0 - login in to the server; 1 - enter the main room self.userState = 0 #:{roomname: roomid} self.roomId = {} #:{userid : username} self.idUser = {} #:{sequenceNumber:typeMessage} self.sequencetotype = {} #: the number of times the same packet was sent self.callCount = 0 #: to store the room name which the client wants to join self.roomName = '' #function callLater self.send = None def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport # def generateSequenceNumber(self): # """ # the sequence number incremented by one for each new packet # the sequence number after 65535 is 0 # """ # self.sequenceNumber+=1 # self.sequenceNumber = self.sequenceNumber % 65535 # return self.sequenceNumber def dec2addr(self, dec): """ Convert hexadecimal to ip address """ return ".".join([str(dec >> x & 0xff) for x in [24, 16, 8, 0]]) def ackResponse(self, datagram): """ When receiving a package from the server, respond to an ack to confirm the reception Here we need to unpack the received datagram, extract the sessionToken, Sequencenumber the payload here is a 0 """ version = 1 typeMessage = 0 sessionToken_1, = struct.unpack('>H', datagram[1:3]) sessionToken_2, = struct.unpack('>B', datagram[3:4]) sequenceNumber, = struct.unpack('>H', datagram[4:6]) payloadSize = 0 buf = struct.pack('>BHBHH', version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize) print('*********Send ACK**************') print("ack :", buf) self.transport.write(buf, (self.serverAddress, self.serverPort)) def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ moduleLogger.debug('loginRequest called with username=%s', userName) # Stop sending more than three times if self.callCount == 3: self.clientProxy.connectionRejectedONE("Connection Lost") version = 1 typeMessage = 1 sessionToken = 0 sessionToken_1 = sessionToken // 256 sessionToken_2 = sessionToken % 256 sequenceNumber = 0 userIdentifier = 0 userLength = len(userName.encode('utf-8')) payloadSize = 4 + len(userName.encode('utf-8')) # Here we create a buffer to store data, buffer is the standard data type after pack. buf = struct.pack('>BHBHHHH%ds' % userLength, version * 16 + typeMessage, sessionToken_1, sessionToken_2, sequenceNumber, payloadSize, userIdentifier, userLength, userName.encode('utf-8')) print('********Send LoginRequest***************') print("sendLoginRequest :", buf) self.transport.write(buf, (self.serverAddress, self.serverPort)) #Store the serial number and type of the sent message self.sequencetotype[sequenceNumber] = [typeMessage] # Here we consider retransmitting this message after timing 1s without receiving ack response. self.callCount += 1 if self.callCount <= 3: #function callLater self.send = reactor.callLater(1, self.sendLoginRequestOIE, userName) # def requestRoomState(self) : # #向服务器发送房间状态的请求 # version = 1 # typeMessage = 3 # sequenceNumber = self.generateSequenceNumber() # payloadSize = 0 # buf = struct.pack('>BHBHH' , version*16+typeMessage, self.sessionToken_1, self.sessionToken_2, # sequenceNumber, payloadSize) # print("requestRoomSteat:" , buf) # self.transport.write(buf,(self.serverAddress,self.serverPort)) # def unpackMovieroomdata(self, datagram): """ unpack the data of the movie room """ # to store the user data userList = [] # to unpack the data in the movie room (roomIdentifier, roomNameLength) = struct.unpack('>HH', datagram[8:12]) roomName, = struct.unpack('>%ds' % roomNameLength, datagram[12:12 + roomNameLength]) roomName = roomName.decode() offset = 12 + roomNameLength (movieIP, moviePort, userNumber) = struct.unpack('>IHH', datagram[offset:offset + 8]) movieIP = self.dec2addr(movieIP) offset += 8 #circuler in order to unpack the data of the user if userNumber != 0: for i in range(userNumber): (userIdentifier, userNameLength) = struct.unpack('>HH', datagram[offset:offset + 4]) offset += 4 userName, = struct.unpack( '>%ds' % userNameLength, datagram[offset:offset + userNameLength]) userName = userName.decode() #to store the identifier and name of other client self.idUser[userIdentifier] = [userName] offset += userNameLength # to add the user to userList userList.append((userName, roomName)) return userList def unpackMainroomdata(self, datagram): """ unpack the data of the main room """ # to store the user data userList = [] #to store the movie data movieList = [] (roomIdentifier, roomNameLength) = struct.unpack('>HH', datagram[8:12]) #the number of user in the main room offset = 18 + roomNameLength userNumber, = struct.unpack('>H', datagram[offset:offset + 2]) offset += 2 #Recycling to unpacke the user data of the main room for i in range(userNumber): (userIdentifier, userNameLength) = struct.unpack('>HH', datagram[offset:offset + 4]) offset += 4 userName, = struct.unpack('>%ds' % userNameLength, datagram[offset:offset + userNameLength]) userName = userName.decode() #to store the identifier and name of other client self.idUser[userIdentifier] = [userName] offset += userNameLength userList.append((userName, ROOM_IDS.MAIN_ROOM)) #unpack the number of movie room movieRoomNumber, = struct.unpack('>H', datagram[offset:offset + 2]) offset += 2 #Recycling to unpacke the data of the movie room for i in range(movieRoomNumber): (movieId, movieNameLength) = struct.unpack('>HH', datagram[offset:offset + 4]) offset += 4 movieName, = struct.unpack( '>%ds' % movieNameLength, datagram[offset:offset + movieNameLength]) movieName = movieName.decode() offset += movieNameLength (movieIP, moviePort, userNumber) = struct.unpack('>IHH', datagram[offset:offset + 8]) movieIP = self.dec2addr(movieIP) offset += 8 # to add roomname and roomid to the dictionary self.roomId[movieName] = [movieId] # to add the movie data to movieList movieList.append((movieName, movieIP, moviePort)) #Recycling to unpacke the user data in the movie room if userNumber == 0: offset += 2 else: for i in range(userNumber): (userIdentifier, userNameLength) = struct.unpack( '>HH', datagram[offset:offset + 4]) offset += 4 userName, = struct.unpack( '>%ds' % userNameLength, datagram[offset:offset + userNameLength]) userName = userName.decode() #to store the identifier and name of other client self.idUser[userIdentifier] = [userName] offset += userNameLength userList.append((userName, movieName)) offset += 2 return userList, movieList def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ # Stop sending more than three times if self.callCount == 3: self.clientProxy.connectionRejectedONE("Connection Lost") version = 1 typeMessage = 6 sequenceNumber = self.sequenceNumber payloadSize = 4 + len(message.encode('utf-8')) #pack the whole message buf = struct.pack('>BHBHHHH%ds' % len(message.encode('utf-8')), version * 16 + typeMessage, self.sessionToken_1, self.sessionToken_2, sequenceNumber, payloadSize, self.userIdentifier, len(message.encode('utf-8')), message.encode('utf-8')) print('********Send Message***************') print("send message:", buf) self.transport.write(buf, (self.serverAddress, self.serverPort)) #Store the serial number and type of the sent message self.sequencetotype[sequenceNumber] = [typeMessage] # Here we consider retransmitting this message after timing 1s without receiving ack response. self.callCount += 1 if self.callCount <= 3: self.send = reactor.callLater(1, self.sendChatMessageOIE, message) def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ # Stop sending more than three times if self.callCount == 3: self.clientProxy.connectionRejectedONE("Connection Lost") self.roomName = roomName version = 1 typeMessage = 5 sequenceNumber = self.sequenceNumber payloadSize = 2 buf = struct.pack('>BHBHHH', version * 16 + typeMessage, self.sessionToken_1, self.sessionToken_2, sequenceNumber, payloadSize, self.roomId[roomName][0]) print('********Send Join Room Request***************') print("JoinRoomRequest:", buf) self.transport.write(buf, (self.serverAddress, self.serverPort)) #Store the serial number and type of the sent message self.sequencetotype[sequenceNumber] = [typeMessage] # Here we consider retransmitting this message after timing 1s without receiving ack response. self.callCount += 1 if self.callCount <= 3: self.send = reactor.callLater(1, self.sendJoinRoomRequestOIE, roomName) def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ # Stop sending more than three times if self.callCount == 3: self.clientProxy.connectionRejectedONE("Connection Lost") version = 1 typeMessage = 7 sequenceNumber = self.sequenceNumber payloadSize = 0 buf = struct.pack('>BHBHH', version * 16 + typeMessage, self.sessionToken_1, self.sessionToken_2, sequenceNumber, payloadSize) print('*********Send Leave System Request**************') print("LeaveSystemRequest:", buf) self.transport.write(buf, (self.serverAddress, self.serverPort)) #Store the serial number and type of the sent message self.sequencetotype[sequenceNumber] = [typeMessage] # Here we consider retransmitting this message after timing 1s without receiving ack response. self.callCount += 1 if self.callCount <= 3: self.send = reactor.callLater(1, self.sendLeaveSystemRequestOIE) def datagramReceived(self, datagram, host_port): """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. """ # Here we extract the first byte of the received data, including #the version and type, where we tend to take out the type. buf, = struct.unpack('>B', datagram[0:1]) typeMessage = buf % 16 (sessionToken_1, sessionToken_2, sequenceNumber) = struct.unpack('>HBH', datagram[1:6]) print('********Datagram Received***************') print("type :", typeMessage) print("sequence number", sequenceNumber) print("datagramReceived :", datagram) # The first step we determine the type of information , if the type is ack, we need to # determine if the ack is received correctly, if the message is correctly,we cancel resend the packet if typeMessage == 0: if sequenceNumber == self.sequenceNumber and sessionToken_1 == self.sessionToken_1 and sessionToken_2 == self.sessionToken_2: print("*******ACK Received******") self.send.cancel() self.callCount = 0 self.sequenceNumber = (self.sequenceNumber + 1) % 65535 # If the type is 7, it means the server has received the request to leave the system, then the customer leaves if self.sequencetotype[sequenceNumber][0] == 7: self.clientProxy.userUpdateReceivedONE( self.userName, ROOM_IDS.OUT_OF_THE_SYSTEM_ROOM) self.clientProxy.leaveSystemOKONE() # If the type is 5, it means the server has received the request to join the room. if self.sequencetotype[sequenceNumber][0] == 5: self.clientProxy.userUpdateReceivedONE( self.userName, self.roomName) self.clientProxy.joinRoomOKONE() #if the type is not an ack, we should respond the server to confirm the reception of the packet # elif typeMessage != 0 and sequenceNumber == self.receive : # self.receive = (self.receive + 1)%65535 else: self.ackResponse(datagram) # typeMessage = 2 means the loginResponse. if typeMessage == 2: print("login response") #take out the responseCode and determine if it is a successful login (responseCode, userIdentifier, userNameLength) = struct.unpack('>BHH', datagram[8:13]) userName, = struct.unpack('>%ds' % userNameLength, datagram[13:13 + userNameLength]) userName = userName.decode() #responseCode = 0, login successful if responseCode == 0: #to store the session token assigned by the server (self.sessionToken_1, self.sessionToken_2) = struct.unpack( '>HB', datagram[1:4]) self.userName = userName self.userIdentifier = userIdentifier #otherwise the login is refused elif responseCode == 1: self.clientProxy.connectionRejectedONE("Invalide User") elif responseCode == 2: self.clientProxy.connectionRejectedONE("Username too long") elif responseCode == 3: self.clientProxy.connectionRejectedONE( "Username not available") elif responseCode == 4: self.clientProxy.connectionRejectedONE( "Service not available") elif responseCode == 255: self.clientProxy.connectionRejectedONE("Unknown Error") # type=6, the client receives the chatMessage by other client if typeMessage == 6 and sessionToken_1 == self.sessionToken_1 and sessionToken_2 == self.sessionToken_2: print("chat message") (userId, messageLength) = struct.unpack('>HH', datagram[8:12]) message, = struct.unpack('>%ds' % messageLength, datagram[12:]) self.clientProxy.chatMessageReceivedONE( self.idUser[userId][0], message.decode()) # type=4, the room state if typeMessage == 4 and sessionToken_1 == self.sessionToken_1 and sessionToken_2 == self.sessionToken_2: print("room state") (roomIdentifier, roomNameLength) = struct.unpack('>HH', datagram[8:12]) roomName, = struct.unpack('>%ds' % roomNameLength, datagram[12:12 + roomNameLength]) roomName = roomName.decode() #the room is Main ROOM if roomName == "Main Room": self.roomId[ROOM_IDS.MAIN_ROOM] = [roomIdentifier] userList, movieList = self.unpackMainroomdata(datagram) #if login successful,initial the room if self.userState == 0: self.clientProxy.initCompleteONE(userList, movieList) self.userState = 1 #update the user list else: self.clientProxy.setUserListONE(userList) #the room is movie room,update the user list else: userList = self.unpackMovieroomdata(datagram) self.clientProxy.setUserListONE(userList)
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address of the c2w server. .. attribute:: serverPort The port number of the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ #: The IP address of the c2w server. self.serverAddress = serverAddress #: The port number of the c2w server. self.serverPort = serverPort #: The clientProxy, which the protocol must use #: to interact with the Graphical User Interface. self.clientProxy = clientProxy self.lossPr = lossPr self.num=0 self.movieList=[] self.userList=[] self.ackReceived ={} self.tryTime={} self.resend={} self.isrefused=False self.isWatching=True #each time we initiliaze the client, the number of sequence begins at 0 def numConstruct(self): #each time we send a sequence, we need to use this function to realize the increasement ot #sequence number numSequence=self.num self.num+=1 return numSequence def datagramSplit(datagram): dataHead=struct.unpack('>I',datagram[0:4]) numSequenceBin=(bin(datahHead[0])[2:].rjust(32,'0')[8:18]) print(numSequenceBin) return(numSequenceBin) def ackConstructor(self): typeAck=80 numSeq=datagramSplit(datagram) longueurAck=longueur[2:].rjust(14,"0") ack0bin=bin(typeAck)[2:].rjust(8,"0")+numSeq+longueurAck ack0=int(ack0bin,2) ack=struct.pack('>I',ack0) print(ack,'ack is constructed') return(ack) def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def headConstruct(self,typeDatagram,numSequence,longueur): #para all three are of the type int print('gihifhsidfhu',numSequence) Type=bin(typeDatagram)[2:].rjust(8,"0") NumSeq=bin(numSequence)[2:].rjust(10,"0") Longueur=bin(longueur)[2:].rjust(14, "0") head=Type+NumSeq+Longueur return(head) def sendAgain(self,datagram,numSeq,host_port): print('12') print(numSeq,datagram,host_port) if self.isrefused==True: return if self.tryTime[numSeq] <= 4 and self.ackReceived[numSeq] == False : print('send agaim') self.transport.write(datagram,host_port) print('resend the datagram',datagram) self.tryTime[numSeq]+=1 reactor.callLater(5,self.sendAgain,datagram,numSeq,host_port) else: if(self.ackReceived[numSeq] == True ): print('this datagram has been responded',datagram) elif(self.tryTime[numSeq] > 4): print('time out for the datagram\'s ack',datagram) def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ numSequence=self.numConstruct() #each time when we need to send a new datagram to server, need to construct a new number of sequence print('number of sequence') head=self.headConstruct(0,numSequence,len(userName)+1) print(head,'head') self.resend[numSequence]=None HeaderUserName=bin(len(userName))[2:].rjust(8,"0") UserNameString='' for i in userName: print(i) BinEach=bin(ord(i)) m=BinEach[2:].rjust(8,"0") UserNameString=UserNameString+m print('test usernamestring',UserNameString) Corps=HeaderUserName+UserNameString datagramBIN=head+Corps print(userName) self.userName=userName print(self.userName,'self de username while logging in ') userName=userName.encode("utf-8") print(int(head,2),len(userName),'test errors in struct') datagram=struct.pack('>Ib%ds'%len(userName),int(head,2),len(userName),userName) self.transport.write(datagram,(self.serverAddress,self.serverPort)) self.tryTime[numSequence]=0 self.ackReceived[numSequence]=False self.resend[numSequence]=reactor.callLater(5,self.sendAgain,datagram,numSequence,(self.serverAddress,self.serverPort)) print(datagram) def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ typeDatagramBin=bin(64)[2:].rjust(8,"0") numSeqInt=self.numConstruct() numSeqBin=bin(numSeqInt)[2:].rjust(10,'0') longueur=len(message)+len(self.userName)+2 longueurBin=bin(longueur)[2:].rjust(14, "0") head=typeDatagramBin+numSeqBin+longueurBin m0=struct.pack('>I',int(head,2)) m1=struct.pack('b%ds'%len(self.userName),len(self.userName),self.userName.encode('utf-8')) m2=struct.pack('b%ds'%len(message),len(message),message.encode('utf-8')) datagram=m0+m1+m2 self.transport.write(datagram,(self.serverAddress,self.serverPort)) print('senda message for chatting to the server',datagram) def sendJoinRoomRequestOIE(self, roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ print('now i am trying to entre a video room',roomName) if(type(roomName)==str): self.isWatching=True self.nowRoom=roomName numSequence=self.numConstruct() print('number of sequence',numSequence) TypeBin=bin(48) Type=TypeBin[2:].rjust(8,"0") NumSeqBin=bin(numSequence) NumSeq=NumSeqBin[2:].rjust(10,"0") print('check the name of room',roomName,type(roomName)) LongueurInt=len(roomName)+1 LongueurBin=bin(LongueurInt) Longueur=LongueurBin[2:].rjust(14, "0") head=Type+NumSeq+Longueur print(head,'head') roomName=roomName.encode("utf-8") datagram=struct.pack('>Ib%ds'%len(roomName),int(head,2),len(roomName),roomName) self.transport.write(datagram,(self.serverAddress,self.serverPort)) print(datagram,'datagram for entring a video room') #self.clientProxy.setUserListONE(('5alice',ROOM_IDS.MOVIE_ROOM)) print('check the join movie room',self.userName, roomName,type(self.userName), type(roomName)) self.clientProxy.userUpdateReceivedONE(self.userName,'threzr') print('is this youdoisuisoq') self.clientProxy.joinRoomOKONE() else: self.isWatching=True print('now i am leaving present movie room') numSequence=self.numConstruct() print('number of sequence',numSequence) TypeBin=bin(49) Type=TypeBin[2:].rjust(8,"0") NumSeqBin=bin(numSequence) NumSeq=NumSeqBin[2:].rjust(10,"0") LongueurInt=0 LongueurBin=bin(LongueurInt) Longueur=LongueurBin[2:].rjust(14, "0") head=Type+NumSeq+Longueur print(head,'head') datagram=struct.pack('>I',int(head,2)) self.transport.write(datagram,(self.serverAddress,self.serverPort)) self.clientProxy.joinRoomOKONE() def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ print('hi now i am leaving this system') self.clientProxy.userUpdateReceivedONE(self.userName,ROOM_IDS.OUT_OF_THE_SYSTEM_ROOM) Type=3 typeLeave=bin(Type)[2:].rjust(8,"0") numSequence=self.num self.num+=1 NumSeqBin=bin(numSequence) NumSeq=NumSeqBin[2:].rjust(10,"0") longueur=bin(0) longueur=longueur[2:].rjust(14,"0") datagram=typeLeave+NumSeq+longueur datagram_leave=struct.pack('>I',int(datagram,2)) self.transport.write(datagram_leave,(self.serverAddress,self.serverPort)) self.clientProxy.applicationQuit() def test(self,datagram): if not(datagram==''): number=datagram[0:2] print( number) number=struct.unpack('>H',number)[0] print('number in room',number) if not(number==0): datagram=datagram[2:] for i in range(number): lenI=struct.unpack('b',datagram[0])[0] print(datagram[3:3+lenI]) list1.append(datagram[1:1+lenI]) datagram=datagram[1+lenI:] self.test(datagram) def buildSalon(self,datagram): print('exam datagram in buildSalon',datagram) if(len(datagram)==2 or len(datagram)==0): return numInRoom=struct.unpack('>H',datagram[0:2])[0] print(numInRoom,'nulber of users in the room presenr') if (numInRoom==0): datagram=datagram[2:] print(datagram,'datagram after 0') if not(numInRoom==0): datagram=datagram[2:] print(datagram,'datagram') for i in range(numInRoom): print('hi') print(datagram[0]) lenUsername=datagram[0] print(type(lenUsername),lenUsername) username=struct.unpack('%ds'%lenUsername,datagram[1:1+lenUsername])[0] self.list1.append(username.decode('utf-8')) datagram=datagram[1+lenUsername:] print('length dqtqgrqm',len(datagram)) print(self.list1,'self.list1') self.buildSalon(datagram) def ackConstructor(self,datagram): typeAck=80 dataHead=struct.unpack('>I',datagram[0:4]) #split the number of sequence in this datagram numSeq=bin(dataHead[0])[2:].rjust(32,'0')[-24:-14] print(numSeq,'number of seq') lengthAck=bin(0)[2:].rjust(14,"0") print(bin(typeAck)[2:].rjust(8,"0"),numSeq,lengthAck,'check ack') ackInt=int(bin(typeAck)[2:].rjust(8,"0")+numSeq+lengthAck,2) ack=struct.pack('>I',ackInt) return ack def getNumSeqFromAck(self,ack): #split the number of sequence from ack datagram unpack=struct.unpack('>I',ack) numSeq=int(bin(unpack[0])[-24:-14],2) return numSeq def datagramReceived(self, datagram, host_port): #time.sleep(6) print('test datagram received ',datagram) typeDatagram=struct.unpack('b',datagram[0:1])[0] print(typeDatagram,'typeDatagram') if(typeDatagram == 80): print('now the server receive an ack from client:',datagram) numSeqAck=self.getNumSeqFromAck(datagram) print('the number of sequence ack is:',numSeqAck) self.ackReceived[numSeqAck] = True if(typeDatagram != 80): print('receive a datagram not an ack?') #self.transport.write(ackConstructor(datagram),host_port) ack=self.ackConstructor(datagram) print('send an ack:',ack,'to be a response of datagram:',datagram) self.transport.write(ack,(self.serverAddress,self.serverPort)) ''' self.transport.write(ack,host_port) ''' # numSeq=datagramSplit(datagram) ''' numSequenceBin=(bin(dataHead[0])[2:].rjust(32,'0')[8:18]) numSeq=numSequenceBin longueur=bin(0) longueurAck=longueur[2:].rjust(14,"0") ack=struct.pack('>I',0) self.transport.write(ack,host_port) ''' ''' ack0bin=bin(typeAck)[2:].rjust(8,"0")+numSeq+longueurAck ack0=int(ack0bin,2) ack=struct.pack('>I',ack0) self.transport.write(ack,host_port) ''' if(typeDatagram == 1):##Connexion etablie print('The connection is created!') #here we need to build the movie list intelligent ici just for a test #movieList=[('Sintel - Trailer', '127.0.0.1', 1040), ('Great Guy', '127.0.0.1', 1090), 'Big Buck Bunny', '127.0.0.1', 1034), ('The Night Before Christmas', '127.0.0.1', 1100), ('Battleship Potemkin', '127.0.0.1', 1070)] #self.clientProxy.initCompleteONE([],movieList) if(typeDatagram== 2):##Connexion echouee self.isrefused=True print('now this admission request is refused') if(typeDatagram == -128): print('receive an error message of type 128',datagram) #erroMessage= error=datagram[5:].decode('utf-8') print(error,type(error)) self.clientProxy.connectionRejectedONE(error) if(typeDatagram ==19):#receive the user list numSalon=datagram[4:6] print(numSalon,'0:split from datagram user list') numSalon=struct.unpack('>H',numSalon)[0] print(numSalon,'split from datagram user list') print('datagram with info user salon',datagram) numSalon=datagram[4:6] numSalon=struct.unpack('>H',numSalon)[0] list0=[] self.list1=[] print(numSalon) numSalonP=datagram[6:8] numSalonP=struct.unpack('>H',numSalonP)[0] print('numSalonP',numSalonP) datagram=datagram[8:] print(datagram,'test0') print('test user list datagram',datagram) if not (numSalonP==0): print('the main room is not vide') for i in range(numSalonP): print(datagram[0],type(datagram[0])) lenUsername=datagram[0] print(lenUsername,'length of username') username=struct.unpack('%ds'%lenUsername,datagram[1:1+lenUsername])[0] print(username,type(username)) list0.append(username.decode('utf-8')) datagram=datagram[1+lenUsername:] print(datagram,len(datagram),2*(numSalon-1),'the last datagram') if (len(datagram)==2*(numSalon-1)): print('no other users in movie room') else: print('anymore') self.buildSalon(datagram) print(list0,'list0',self.list1,'list1') self.userList=[] if not (len(list0)==0): print('0test',self.userList) for i in list0: self.userList.append((i,ROOM_IDS.MAIN_ROOM)) if not (len(self.list1)==0): print('1test',self.userList) for i in self.list1: self.userList.append((i,ROOM_IDS.MOVIE_ROOM)) #self.clientProxy.setUserListONE(self.userList) print(self.userList,'test self.userList for setOne') if(len(self.movieList)>0): self.clientProxy. setUserListONE(self.userList) print('show me the result of userList',self.userList) if(typeDatagram ==16):#receive the movie list print('receive movie list for server') head=struct.unpack('>IH',datagram[0:6]) print('head after unpacking',head) numMovie=int(head[1]) corps=datagram[6:] print('corps of movieLIst',corps) head=bin(head[0]).rjust(32,"0") print('datargame head',head) print('check number of movie',numMovie) for i in range(numMovie): print('hiiii',corps[0]) lenMovieName=corps[0] print(i,type(i)) print('movie name 555',corps[1:1+lenMovieName]) movieName=corps[1:1+lenMovieName] print('movie name',str(movieName)) print('movie name',type(movieName)) print('test movie name',str(movieName[2:])) movieName=movieName.decode("utf-8") ip=struct.unpack('4b',corps[1+lenMovieName:5+lenMovieName]) print(ip) ipStr=str(ip[0]) for i in ip[1:]: ipStr+='.'+str(i) print('test string ip',ipStr) port=struct.unpack('>H',corps[5+lenMovieName:7+lenMovieName]) print(port,'test port') print(int(port[0])) port=int(port[0]) corps=corps[7+lenMovieName:] print(str(movieName),'hhhhh') self.movieList.append((movieName,ipStr,port)) print('check if movie list is correct',self.movieList) print('check if movie list is correct',self.userList) print('head',head) print(self.movieList,'if the movie list unpack by the client if correct',self.userList) self.clientProxy.initCompleteONE(self.userList,self.movieList) if(typeDatagram ==18): print('receive 18 datagram:',datagram) numSame=datagram[5] listRoom=[] print('number of users in the same room',numSame) l=datagram[6:] for i in range(numSame): lenName=l[0] name=struct.unpack('%ds'%lenName,l[1:1+lenName])[0].decode('utf-8') print('name test',name) listRoom.append((name,self.nowRoom)) l=l[1+lenName:] print('test listRoom',listRoom) self.clientProxy.setUserListONE(listRoom) if(typeDatagram ==65): print('recieve 65',datagram) lenUsername=datagram[4] userName=struct.unpack('%ds'%lenUsername,datagram[5:5+lenUsername])[0] lenmss=datagram[5+lenUsername] print(datagram[5+lenUsername+lenmss:],len(datagram[5+lenUsername+1:])) mess=struct.unpack('%ds'%lenmss,datagram[6+lenUsername:])[0] print('the user',userName,type(userName), 'is send here',mess,type(mess)) self.clientProxy.chatMessageReceivedONE(userName.decode('utf-8'), mess.decode('utf-8')) else: print('no case satisfied') ''' if(typeDatagram == 1):##Connexion etablie print('The connection is created!') #self.clientProxy.initCompleteONE(userList,movieList) if(typeDatagram == 2):##Connexion echouee self.clientProxy.connectionRejectedONE('userName exists') print('Error connection(same userName)!') ''' """ :param string datagram: the payload of the UDP packet. :param host_port: a touple containing the source IP address and port. Called **by Twisted** when the client has received a UDP packet. when we receive a datagram, firstly we need to split the datagram to clearly see the part of Type, NumSeq, Longueur, Corps """ """except we receive an acquitement, we must send back a message of acquitement
class c2wUdpChatClientProtocol(DatagramProtocol): def __init__(self, serverAddress, serverPort, clientProxy, lossPr): """ :param serverAddress: The IP address (or the name) of the c2w server, given by the user. :param serverPort: The port number used by the c2w server, given by the user. :param clientProxy: The clientProxy, which the protocol must use to interact with the Graphical User Interface. Class implementing the UDP version of the client protocol. .. note:: You must write the implementation of this class. Each instance must have at least the following attributes: .. attribute:: serverAddress The IP address (or the name) of the c2w server. .. attribute:: serverPort The port number used by the c2w server. .. attribute:: clientProxy The clientProxy, which the protocol must use to interact with the Graphical User Interface. .. attribute:: lossPr The packet loss probability for outgoing packets. Do not modify this value! (It is used by startProtocol.) .. note:: You must add attributes and methods to this class in order to have a working and complete implementation of the c2w protocol. """ self.serverAddress = serverAddress self.serverPort = serverPort self.clientProxy = clientProxy self.lossPr = lossPr self.idcall = None self.status = None self.userID = None self.seq_num =0 self.expected_seq_num=0 self.listOfUserName =[] self.listOfUserId = () self.userNames=[] self.listOfTitles=[] self.listOfMovieId=[] self.usernamesUpdated=0 self.listOfMovies=[] self.NbrMovie = None self.NbrUser = None self.current_movie_room_id=None self.requested_movie_room_id=None self.type_last_message=None self.connected= False self.requestMovieName = None self.MovieName = None def startProtocol(self): """ DO NOT MODIFY THE FIRST TWO LINES OF THIS METHOD!! If in doubt, do not add anything to this method. Just ignore it. It is used to randomly drop outgoing packets if the -l command line option is used. """ self.transport = LossyTransport(self.transport, self.lossPr) DatagramProtocol.transport = self.transport def sendLoginRequestOIE(self, userName): """ :param string userName: The user name that the user has typed. The client proxy calls this function when the user clicks on the login button. """ if type(userName)==str : moduleLogger.debug('loginRequest called with username=%s', userName) TYPE = '0001' self.type_last_message=int(TYPE,2) A = '0' R = '0' ACK_NUM = 0 DATA=userName buffer_length = 6 + len(DATA) buf = ctypes.create_string_buffer(buffer_length) fourbyte = (int(TYPE,2) << 28) + (int(A,2) << 27) + (int(R,2) << 26) + (int(str(ACK_NUM),2) << 13) + (int(str(self.seq_num),2) << 0) struct.pack_into('>IH'+str(len(DATA))+'s',buf,0,fourbyte, buffer_length,DATA) self.transport.write(buf.raw,(self.serverAddress,self.serverPort)) self.idcall = reactor.callLater(3.0,self.sendLoginRequestOIE,[userName]) else : if type(userName)==list : if not(userName==[]): first_username=userName[0] self.sendLoginRequestOIE(first_username) userName.pop(0) self.sendLoginRequestOIE(userName) else: return def sendChatMessageOIE(self, message): """ :param message: The text of the chat message. :type message: string Called by the client proxy when the user has decided to send a chat message .. note:: This is the only function handling chat messages, irrespective of the room where the user is. Therefore it is up to the c2wChatClientProctocol or to the server to make sure that this message is handled properly, i.e., it is shown only by the client(s) who are in the same room. """ ACK_NUM = 0; self.send_Public_Message(ACK_NUM,self.seq_num,self.userID,message) self.idcall = reactor.callLater(3.0,self.sendChatMessageOIE,[message]) def sendJoinRoomRequestOIE(self,roomName): """ :param roomName: The room name (or movie title.) Called by the client proxy when the user has clicked on the watch button or the leave button, indicating that she/he wants to change room. .. warning: The controller sets roomName to c2w.main.constants.ROOM_IDS.MAIN_ROOM when the user wants to go back to the main room. """ ACK_NUM = 0; if(roomName == ROOM_IDS.MAIN_ROOM): self.requested_movie_room_id= 0 self.status = 1 self.MovieName = ROOM_IDS.MAIN_ROOM self.send_MainRoom_Request(ACK_NUM,self.seq_num,self.userID) else: indexOfRoom = self.listOfTitles.index(roomName) self.MovieName = roomName self.status = 0 movieId = self.listOfMovieId[indexOfRoom] self.send_MovieRoom_Request(ACK_NUM,self.seq_num,self.userID,movieId) self.idcall = reactor.callLater(3.0,self.sendJoinRoomRequestOIE,[roomName]) def sendLeaveSystemRequestOIE(self): """ Called by the client proxy when the user has clicked on the leave button in the main room. """ ACK_NUM = 0 self.disconnect(ACK_NUM,self.seq_num,self.userID) self.clientProxy.applicationQuit() self.idcall = reactor.callLater(3.0,self.sendLeaveSystemRequestOIE) def datagramReceived(self, data, (host, port)): """ :param data: The message received from the server :type data: A string of indeterminate length Twisted calls this method whenever new data is received on this connection. """ j=struct.unpack_from('!I',data[0:])[0] if(j==12): l=data[4:].split('.') for item in l: if not(item==''): self.listOfMovieId.append(int(item)) else: dataParsed = core.parseReceivedData(data) TYPE = dataParsed[0] data_extracted = dataParsed[6] if (TYPE == 2 and self.connected == False): self.AnalyseUserMessage(data_extracted) self.clientProxy.initCompleteONE(self.listOfUserName,self.listOfMovies) self.sendAck(dataParsed[5]) self.connected =True elif(TYPE == 2 and self.connected ==True and self.status == 1): self.AnalyseUserMessage(data_extracted) self.clientProxy.setUserListONE(self.listOfUserName) elif(TYPE == 2 and self.connected ==True and self.status == 0): self.AnalyseUserMessageForMovieRoom(self.MovieName,data_extracted) self.clientProxy.setUserListONE(self.listOfUserName) elif (TYPE == 3): self.AnalyseMovieMessage(data_extracted) self.sendAck(dataParsed[5]) elif (TYPE == 4): PublicMessage_infos= core.extract_public_message(data_extracted) userId = PublicMessage_infos[0] indiceUser = self.listOfUserId.index(userId) username=self.listOfUserName[indiceUser][0] self.clientProxy.chatMessageReceivedONE(username,PublicMessage_infos[2]) self.sendAck(dataParsed[5]) elif (TYPE == 5): PrivateMessage_infos = core.extract_private_message(data_extracted) userId = PrivateMessage_infos[0] indiceUser = self.listOfUserId.index(userId) username=self.listOfUserName[indiceUser][0] self.clientProxy.chatMessageReceivedONE(username,PrivateMessage_infos[2]) self.sendAck(dataParsed[5]) elif (TYPE == 14): self.idcall.cancel() self.ReceiveLoginResponse(data_extracted) self.sendAck(dataParsed[5]) elif (TYPE == 15): self.idcall.cancel() self.seq_num +=1 if self.type_last_message == 8: self.clientProxy.leaveSystemOKONE() elif (self.type_last_message == 6 ): self.current_movie_room_id=self.requested_movie_room_id self.clientProxy.joinRoomOKONE() elif ( self.type_last_message == 7): self.current_movie_room_id=self.requested_movie_room_id self.clientProxy.joinRoomOKONE() elif (TYPE == 0): self.extract_error(data_extracted)