def get_name(self): #do this until we have a name while self.name == '': #prompt user msg_name_prompt = message.Message() msg_name_prompt.clear() msg_name_prompt.set_text('Please input your name') self.send(message.pack(msg_name_prompt)) #get name from socket msg_client_name = message.Message(data=message.unpack(self.conn.recv(DATA_SIZE))) while msg_client_name.get_type() == 'ping': self.handle_ping() msg_client_name = message.Message(data=message.unpack(self.conn.recv(DATA_SIZE))) name_from_client = msg_client_name.get_text() #check name length if len(name_from_client) > NAME_SIZE_LIMIT: msg_name_too_long = message.Message() msg_name_too_long.set_text('That name is too long') self.send(message.pack(msg_name_too_long)) else: #check name isnt already taken name_taken = False for c in CLIENTS: if name_from_client == c.name: name_taken = True if name_taken: msg_name_taken = message.Message() msg_name_taken.set_text('That name is already taken') self.send(message.pack(msg_name_taken)) else: #if we pass all the tests then set the clien name self.name = name_from_client
def handle_user_command(client, msg): cmds = msg.get_text().split() if cmds[0] == '/name': prev_name = client.name_color + client.name client.name = '' client.get_name() msg_name_change = message.Message() msg_name_change.set_time(get_time()) msg_name_change.set_text('{} changed their name to {}'.format(prev_name, client.name)) broadcast(message.pack(msg_name_change)) return True elif cmds[0] == '/color': if cmds[1] in USER_COLORS: print('set {} color to red'.format(client)) client.name_color = cmds[1] else: client.name_color = None return True elif cmds[0] == '/clan': if len(cmds) > 1: if cmds[1] == 'leave': client.clan = None return True elif len(cmds[1]) <= 4: client.clan = cmds[1] return True return False elif cmds[0] == '/help': msg_help = message.Message() msg_help.set_type('text') msg_help.set_text('\n\t/help - show help\n\t/name <new name> - change your name\n\t/color [red, green, yellow, blue, purple, cyan] - change name color WIP\n\t/clan leave/<4 characters> - leave or join a clan') client.send(message.pack(msg_help)) return True return False
def main(): print('\033[1;37;40mType \'/quit\' to leave') global RUNNING #specify connection attempts loop for connection_attempts in range(0, 10): try: #create socket and connect to the server s = socket.socket() s.connect((IP_ADDR, PORT)) s.settimeout(10) #if we havent failed yet then reset out connection_attempts counter connection_attempts = 0 #start a thread to listen for messages from the server listen_thread = threading.Thread(target=listen_loop, args=(s, )) listen_thread.start() #start a thread to listen for pings from the server to verify connection ping_thread = threading.Thread(target=ping_loop, args=(s, )) ping_thread.start() #listen for CLI input while RUNNING: input_text = input() #if the user wants to quit, we should quit if input_text == '/quit': msg_leave = message.Message() msg_leave.set_type('leave') s.sendall(message.pack(msg)) RUNNING = False break #if it is a message to the server msg = message.Message() msg.set_type('text') msg.set_text(input_text) s.sendall(message.pack(msg)) #cleanup listen_thread.join() ping_thread.join() s.shutdown() except: #handle exceptions print('No Connection.. Retrying') time.sleep(10) #say goodbye depending on how it shut down if RUNNING: print('Connection Failed!!!') else: print('Good bye!')
def run(self): try: #get the name from the client self.get_name() #tell everyone there is a new client msg_user_join = message.Message() msg_user_join.set_type('text') msg_user_join.set_text('{} has joined the chat!'.format(self.name)) msg_user_join.set_time(get_time()) print(message.pack(msg_user_join)) broadcast(message.pack(msg_user_join)) #add this client to the client list CLIENTS.append(self) msg_data = self.conn.recv(DATA_SIZE) while msg_data: msg = message.Message(data=message.unpack(msg_data)) if msg.get_text() != None: msg.set_time(time.strftime('%H:%M:%S', time.localtime())) msg.set_sender(self.name, color=self.name_color, clan=self.clan) if msg.get_type() == 'ping': self.handle_ping() elif msg.get_type() == 'leave': self.leave() elif msg.get_type() == 'text': if msg.get_text().split()[0] in USER_COMMANDS: if not handle_user_command(self, msg): msg_invalid_command = message.Message() msg_invalid_command.set_type('text') msg_invalid_command.set_text('Invalid command. view /help') self.send(message.pack(msg_invalid_command)) print('PASS') else: if len(msg.get_text()) > MSG_SIZE_MAX: error_msg = message.Message() error_msg.set_type('error') error_msg.set_sender('[SERVER]') error_msg.set_time(get_time()) error_msg.set_text('Your message was too long. Max is 200 characters.') self.send(message.pack(error_msg)) print('{} attempted to send a LARGE message'.format(self.address)) else: #the formatted message as it will be sent to all users msg.print() broadcast(message.pack(msg)) else: print('Unknown message type: {}'.format(message.unpack(msg))) msg_data = self.conn.recv(DATA_SIZE) except Exception as e: print(e) print("Connection Lost to {}".format(self.address)) self.leave()
def ping_loop(socket): while RUNNING: time.sleep(3) ping_msg = message.Message() ping_msg.set_type('ping') bmsg = message.pack(ping_msg) socket.sendall(bmsg)
def get_name(self): #do this until we have a name while self.name == '': #prompt user msg_name_prompt = message.Message() msg_name_prompt.clear() msg_name_prompt.set_text('Please input your name') self.send(message.pack(msg_name_prompt)) try: #get name from socket while the message received is not a ping msg_client_name = message.Message(data=message.unpack(self.conn.recv(DATA_SIZE))) while msg_client_name.get_type() == 'ping': self.handle_ping() msg_client_name = message.Message(data=message.unpack(self.conn.recv(DATA_SIZE))) name_from_client = msg_client_name.get_text() except: #if the data from the client is malformed then restart this process break #verify name length if len(name_from_client) > NAME_SIZE_LIMIT: # if the name is too long restart msg_name_too_long = message.Message() msg_name_too_long.set_type('text') msg_name_too_long.set_text('That name is too long. Max: {} characters'.format(NAME_SIZE_LIMIT)) self.send(message.pack(msg_name_too_long)) else: #verify name isnt already taken name_taken = False for c in CLIENTS: if name_from_client == c.name: name_taken = True # if the name is taken then restart if name_taken: msg_name_taken = message.Message() msg_name_taken.set_type('text') msg_name_taken.set_text('That name is already taken') self.send(message.pack(msg_name_taken)) else: #if we pass all the tests then set the clien name self.name = name_from_client
def handle_loop_message(self, loop_payload, ttl): '''This function will process the loop message and will determine if the message should be passed to the next stage (the next process) or it should be discarted. (Returning True or False). ''' type = struct.unpack(">B", loop_payload[0])[0] if type == passage.LOOP_SUBTYPE_BY_NAME['Leader']: syslog.syslog(syslog.LOG_DEBUG, "Leader Election received...") leader_name_len = struct.unpack('>B', loop_payload[1])[0] leader_name = struct.unpack('%is' % leader_name_len, loop_payload[2: leader_name_len+2])[0] if leader_name == self.localhost_name: syslog.syslog(syslog.LOG_DEBUG, "I'am the new leader %s (previous %s) of the group with %i members." % (str(self.localhost_name), str(self.leader_name), passage.TTL - ttl + 1)) #Stop the leader algorithm. # # The inbound process MUST start sending its localname as leadername to the outbound queue. # When that message come back to the outbound, then the algorithm finish and localname is the leadername self.leader_name = leader_name if self.leader_process and self.leader_process.poll() is None: return False #iam the leader already self.clean() self.leader_process = Popen(["python", "leader.py", self.path, self.char_id_out, str(self.group_id), self.localhost_name, self.network_name]) #XXX Who has the token? self.userland_inbound_queue.push(message.pack("\x00"*(4*3 + 256), passage.ID_BY_TYPE['USER'])) return False elif leader_name < self.localhost_name: syslog.syslog(syslog.LOG_DEBUG, "Minor leader proposal %s discarted by %s (actual leader %s)." % (str(leader_name), str(self.localhost_name), str(self.leader_name) )) return False else: syslog.syslog(syslog.LOG_DEBUG, "Mayor leader proposal %s exceeds me %s (previous leader %s)." % (str(leader_name), str(self.localhost_name), str(self.leader_name) )) self.clean() self.leader_name = leader_name return True elif type == passage.LOOP_SUBTYPE_BY_NAME['LinkBroken']: open_node_name_len = struct.unpack('>B', loop_payload[1])[0] open_node_name = struct.unpack('%is' % open_node_name_len, loop_payload[2: open_node_name_len+2])[0] if open_node_name == self.localhost_name: return False else: self.clean() return True else: #Tipo incorrecto, como llego aqui?!? raise Exception return False
def handle_user_command(client, msg): # break the text into components of the command cmds = msg.get_text().split() ## NAME COMMAND if cmds[0] == '/name': prev_name = client.name_color + client.name client.name = '' client.get_name() # this code tells the chat room that a user changed their name # by popular demand this was removed # msg_name_change = message.Message() # msg_name_change.set_time(get_time()) # msg_name_change.set_type('text') # msg_name_change.set_text('{} changed their name to {}'.format(prev_name, client.name)) # broadcast(message.pack(msg_name_change)) return True ## NAME COLOR COMMAND elif cmds[0] == '/color': if len(cmds) > 1: if cmds[1] in USER_COLORS: #print('set {} color to red'.format(client.name)) client.name_color = cmds[1] else: client.name_color = None else: client.name_color = None return True ## CLAN COMMAND elif cmds[0] == '/clan': if len(cmds) > 1: if cmds[1] == 'leave': client.clan = None return True elif len(cmds[1]) <= 4: client.clan = cmds[1] return True return False ## HELP COMMAND elif cmds[0] == '/help': msg_help = message.Message() msg_help.set_type('text') msg_help.set_text('\n\t/help - show help\n\t/name <new name> - change your name\n\t/color [red, green, yellow, blue, purple, cyan] - change name color WIP\n\t/clan leave/<4 characters> - leave or join a clan') client.send(message.pack(msg_help)) return True return False
def main(): #start the server server_socket = start_server() global RUNNING #start the client accept thread accept_thread = threading.Thread(target=accept_clients, args=(server_socket, )) accept_thread.start() #handle server command line input while RUNNING: user_input = input() if user_input.startswith('/stop'): RUNNING = False break elif user_input.startswith('/say'): msg_server_broadcast = message.Message() msg_server_broadcast.set_type('text') msg_server_broadcast.set_text(user_input[5:]) msg_server_broadcast.set_sender('[SERVER]') broadcast(message.pack(msg_server_broadcast)) accept_thread.stop()
def run(self): try: #get the name from the client self.get_name() #tell everyone there is a new client msg_user_join = message.Message() msg_user_join.set_type('text') msg_user_join.set_text('{} has joined the chat!'.format(self.name)) msg_user_join.set_time(get_time()) broadcast(message.pack(msg_user_join)) #add this client to the client list CLIENTS.append(self) # main data listen loop msg_data = self.conn.recv(DATA_SIZE) while msg_data: # turn the data into a message msg = message.Message(data=message.unpack(msg_data)) if msg.get_text() != None: # set the values of the message such as the time and the sender msg.set_time(get_time()) msg.set_sender(self.name, color=self.name_color, clan=self.clan) #handle message types if msg.get_type() == 'ping': self.handle_ping() elif msg.get_type() == 'leave': self.leave() elif msg.get_type() == 'text': #check if the msg the user sent is a command if msg.get_text().split()[0] in USER_COMMANDS: #handle the user command if not handle_user_command(self, msg): #if the user command fails #tell the user msg_invalid_command = message.Message() msg_invalid_command.set_type('text') msg_invalid_command.set_text('Invalid command. view /help') self.send(message.pack(msg_invalid_command)) else: #check the text from the user is smaller than the max acceptable size if len(msg.get_text()) > MSG_SIZE_MAX: error_msg = message.Message() error_msg.set_type('error') error_msg.set_sender('[SERVER]') error_msg.set_time(get_time()) error_msg.set_text('Your message was too long. Max is 200 characters.') self.send(message.pack(error_msg)) print('{} attempted to send a LARGE message'.format(self.address)) else: #broadcast the message to the clients broadcast(message.pack(msg)) if SHOW_CLIENT_MESSAGES: print(msg) else: #handle unknown cases print('Unknown message type: {}'.format(message.unpack(msg))) #continue to listen for data from the client msg_data = self.conn.recv(DATA_SIZE) except Exception as e: #handle exceptions #i am not the most familiar with the python exceptions #so im printing as much information to both learn and debug #this does lead to a messy server console exc_information = sys.exc_info() print(exc_information) print("Connection Lost to {}".format(self.address)) self.leave()
def passage_inbound_messages(inbound_socket, userland_inbound_queue, userland_outbound_queue, driver): try: inbound_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) inbound_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) inbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, KEEPALIVE_PROBE_COUNT) inbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, KEEPALIVE_IDLE_TIMEOUT) inbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, KEEPALIVE_RETRY_SEND_PROBE) peer = inbound_socket.getpeername() while True: try: start_receiving = False syslog.syslog(syslog.LOG_INFO, "Receiving packet from remote node %s..." % str(peer)) type = _recv(inbound_socket, 4, peer) start_receiving = True if type not in ID_BY_TYPE.keys(): raise InvalidNetworkMessage("The message received from a member of the ring has a wrong type (corrupted message).", type, peer) size = struct.unpack('>H', _recv(inbound_socket, 2, peer))[0] if size > MAX_PAYLOAD: raise InvalidNetworkMessage("The message received from a member of the ring has a wrong value in the 'size' field (corrupted message).", size, peer) payload = _recv(inbound_socket, size, peer) _send(inbound_socket, "A", peer) if type == 'LOOP': ttl = struct.unpack('>H', payload[:2])[0] syslog.syslog(syslog.LOG_DEBUG, "LOOP packet recieved (TTL %i): %s [%s]" % (ttl, " ".join(map(lambda c: hex(ord(c)), payload[2:])), "".join([(c if ord('0') <= ord(c) <= ord('Z') else '.') for c in payload[2:]]))) if ttl <= 0: syslog.syslog(syslog.LOG_INFO, "LOOP packet (TTL %i) discarted." % ttl) continue if driver.handle_loop_message(payload[2:], ttl): syslog.syslog(syslog.LOG_INFO, "Moving LOOP packet to output queue.") payload = struct.pack('>H', ttl - 1) + payload[2:] userland_outbound_queue.push(message.pack(payload, ID_BY_TYPE[type])) elif type == 'USER': syslog.syslog(syslog.LOG_INFO, "Moving USER packet to the user (up): %s" % (" ".join(map(lambda c: hex(ord(c)), payload)))) userland_inbound_queue.push(message.pack(payload, ID_BY_TYPE[type])) else: raise InvalidNetworkMessage("The message received from a member of the ring was corrupted.", payload, peer) except socket.error, e: if start_receiving: syslog.syslog(syslog.LOG_INFO, "Packet received partially because some connection error.") else: syslog.syslog(syslog.LOG_INFO, "No packet received because some connection error.") raise UnstableChannel("The other side (other peer) is not responding to the probes or an internal error happen.", peer, e) finally: try: inbound_socket.shutdown(2) except socket.error: pass inbound_socket.close()
def create_leader_proposal_msj(localhost_name): s = struct.pack(">HBB%is" % (len(localhost_name)), passage.TTL, passage.LOOP_SUBTYPE_BY_NAME['Leader'], len(localhost_name), localhost_name) return message.pack(s, passage.ID_BY_TYPE['LOOP'])
if __name__ == '__main__': print('\033[1;37;40mType \'quit\' to leave') s = socket.socket() s.connect((IP_ADDR, PORT)) s.settimeout(10) listen_thread = threading.Thread(target=listen_loop, args=(s,)) listen_thread.start() ping_thread = threading.Thread(target=ping_loop, args=(s,)) ping_thread.start() while RUNNING: input_text = input() if input_text == 'quit': msg_leave = message.Message() msg_leave.set_type('leave') s.sendall(message.pack(msg)) RUNNING = False break #if it is a message to the server msg = message.Message() msg.set_type('text') msg.set_text(input_text) s.sendall(message.pack(msg)) listen_thread.join() s.shutdown()
def create_linkbroken_msj(self): s = struct.pack(">HBB%is" % (len(localhost_name)), passage.TTL, passage.LOOP_SUBTYPE_BY_NAME['LinkBroken'], len(localhost_name), localhost_name) return message.pack(s, passage.ID_BY_TYPE['LOOP'])
def create_breaklink_msj(): s = struct.pack(">HB", 0, passage.LOOP_SUBTYPE_BY_NAME['BreakLinkForced']) return message.pack(s, passage.ID_BY_TYPE['LOOP'])
def handle_ping(self): #print('ping from {}'.format(self.address)) msg_ping_resp = message.Message() msg_ping_resp.set_type('ping') self.send(message.pack(msg_ping_resp))
def handle_loop_message(self, loop_payload, ttl): '''This function will process the loop message and will determine if the message should be passed to the next stage (the next process) or it should be discarted. (Returning True or False). ''' type = struct.unpack(">B", loop_payload[0])[0] if type == passage.LOOP_SUBTYPE_BY_NAME['Leader']: syslog.syslog(syslog.LOG_DEBUG, "Leader Election received...") leader_name_len = struct.unpack('>B', loop_payload[1])[0] leader_name = struct.unpack('%is' % leader_name_len, loop_payload[2:leader_name_len + 2])[0] if leader_name == self.localhost_name: syslog.syslog( syslog.LOG_DEBUG, "I'am the new leader %s (previous %s) of the group with %i members." % (str(self.localhost_name), str( self.leader_name), passage.TTL - ttl + 1)) #Stop the leader algorithm. # # The inbound process MUST start sending its localname as leadername to the outbound queue. # When that message come back to the outbound, then the algorithm finish and localname is the leadername self.leader_name = leader_name if self.leader_process and self.leader_process.poll() is None: return False #iam the leader already self.clean() self.leader_process = Popen([ "python", "leader.py", self.path, self.char_id_out, str(self.group_id), self.localhost_name, self.network_name ]) #XXX Who has the token? self.userland_inbound_queue.push( message.pack("\x00" * (4 * 3 + 256), passage.ID_BY_TYPE['USER'])) return False elif leader_name < self.localhost_name: syslog.syslog( syslog.LOG_DEBUG, "Minor leader proposal %s discarted by %s (actual leader %s)." % (str(leader_name), str( self.localhost_name), str(self.leader_name))) return False else: syslog.syslog( syslog.LOG_DEBUG, "Mayor leader proposal %s exceeds me %s (previous leader %s)." % (str(leader_name), str( self.localhost_name), str(self.leader_name))) self.clean() self.leader_name = leader_name return True elif type == passage.LOOP_SUBTYPE_BY_NAME['LinkBroken']: open_node_name_len = struct.unpack('>B', loop_payload[1])[0] open_node_name = struct.unpack( '%is' % open_node_name_len, loop_payload[2:open_node_name_len + 2])[0] if open_node_name == self.localhost_name: return False else: self.clean() return True else: #Tipo incorrecto, como llego aqui?!? raise Exception return False
def leave(self): CLIENTS.remove(self) msg_user_left = message.Message() msg_user_left.set_time(get_time()) msg_user_left.set_text('{} left the chat'.format(self.name)) broadcast(message.pack(msg_user_left))
def passage_inbound_messages(inbound_socket, userland_inbound_queue, userland_outbound_queue, driver): try: inbound_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) inbound_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) inbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, KEEPALIVE_PROBE_COUNT) inbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, KEEPALIVE_IDLE_TIMEOUT) inbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, KEEPALIVE_RETRY_SEND_PROBE) peer = inbound_socket.getpeername() while True: try: start_receiving = False syslog.syslog( syslog.LOG_INFO, "Receiving packet from remote node %s..." % str(peer)) type = _recv(inbound_socket, 4, peer) start_receiving = True if type not in ID_BY_TYPE.keys(): raise InvalidNetworkMessage( "The message received from a member of the ring has a wrong type (corrupted message).", type, peer) size = struct.unpack('>H', _recv(inbound_socket, 2, peer))[0] if size > MAX_PAYLOAD: raise InvalidNetworkMessage( "The message received from a member of the ring has a wrong value in the 'size' field (corrupted message).", size, peer) payload = _recv(inbound_socket, size, peer) _send(inbound_socket, "A", peer) if type == 'LOOP': ttl = struct.unpack('>H', payload[:2])[0] syslog.syslog( syslog.LOG_DEBUG, "LOOP packet recieved (TTL %i): %s [%s]" % (ttl, " ".join(map( lambda c: hex(ord(c)), payload[2:])), "".join( [(c if ord('0') <= ord(c) <= ord('Z') else '.') for c in payload[2:]]))) if ttl <= 0: syslog.syslog(syslog.LOG_INFO, "LOOP packet (TTL %i) discarted." % ttl) continue if driver.handle_loop_message(payload[2:], ttl): syslog.syslog(syslog.LOG_INFO, "Moving LOOP packet to output queue.") payload = struct.pack('>H', ttl - 1) + payload[2:] userland_outbound_queue.push( message.pack(payload, ID_BY_TYPE[type])) elif type == 'USER': syslog.syslog( syslog.LOG_INFO, "Moving USER packet to the user (up): %s" % (" ".join(map(lambda c: hex(ord(c)), payload)))) userland_inbound_queue.push( message.pack(payload, ID_BY_TYPE[type])) else: raise InvalidNetworkMessage( "The message received from a member of the ring was corrupted.", payload, peer) except socket.error, e: if start_receiving: syslog.syslog( syslog.LOG_INFO, "Packet received partially because some connection error." ) else: syslog.syslog( syslog.LOG_INFO, "No packet received because some connection error.") raise UnstableChannel( "The other side (other peer) is not responding to the probes or an internal error happen.", peer, e) finally: try: inbound_socket.shutdown(2) except socket.error: pass inbound_socket.close()