def __init__(self, repo_path): self.open_portforward(True) self.s = None; self.is_connected=False self.send_socket = None; self.repo_path = repo_path self.username = None; self.app_start_time = time.time() self.logical_clk_time = 0; self.music_counter = [0, ]; self.music_table = {} self.session_table = {} self.file_table = {} self.music_info_object = Music_Info() self.music_info, self.file_table = self.music_info_object.read_repo(self.music_counter, self.repo_path) self.music_table_lock = threading.Lock() self.session_table_lock = threading.Lock() self.logical_clk_lock = threading.Lock() self.music_info_lock = threading.Lock() self.detectlost_lock = threading.Lock() self.connection_server='' self.connection_state=False self.normal_shutdown=False self.logical_time=0 self.poll_event=threading.Event() self.hb_event=threading.Event() self.received_hb={} self.connect_server() self.thread_client_receive = threading.Thread(target=self.receive_client) self.thread_client_receive.start() self.thread_client_HB = threading.Thread(target=self.period_CCHB) self.thread_client_HB.start() self.thread_server_HB = threading.Thread(target=self.send_hb) self.thread_server_HB.start() self.thread_client_DL = threading.Thread(target=self.detectLost) self.thread_client_DL.start() self.thread_poll_server= threading.Thread(target=self.poll_server) self.thread_poll_server.start() self.thread_client_liveness = threading.Thread(target=self.client_liveness_check) self.thread_client_liveness.start() self.player=Player(self,)
class client(object): def __init__(self, repo_path): self.open_portforward(True) self.s = None; self.is_connected=False self.send_socket = None; self.repo_path = repo_path self.username = None; self.app_start_time = time.time() self.logical_clk_time = 0; self.music_counter = [0, ]; self.music_table = {} self.session_table = {} self.file_table = {} self.music_info_object = Music_Info() self.music_info, self.file_table = self.music_info_object.read_repo(self.music_counter, self.repo_path) self.music_table_lock = threading.Lock() self.session_table_lock = threading.Lock() self.logical_clk_lock = threading.Lock() self.music_info_lock = threading.Lock() self.detectlost_lock = threading.Lock() self.connection_server='' self.connection_state=False self.normal_shutdown=False self.logical_time=0 self.poll_event=threading.Event() self.hb_event=threading.Event() self.received_hb={} self.connect_server() self.thread_client_receive = threading.Thread(target=self.receive_client) self.thread_client_receive.start() self.thread_client_HB = threading.Thread(target=self.period_CCHB) self.thread_client_HB.start() self.thread_server_HB = threading.Thread(target=self.send_hb) self.thread_server_HB.start() self.thread_client_DL = threading.Thread(target=self.detectLost) self.thread_client_DL.start() self.thread_poll_server= threading.Thread(target=self.poll_server) self.thread_poll_server.start() self.thread_client_liveness = threading.Thread(target=self.client_liveness_check) self.thread_client_liveness.start() self.player=Player(self,) def open_portforward(self,value): if value: self.unified_port=False #................For telling bootstrap server................... self.public_ip=str(self.get_real_ip()) self.public_map_port=40001 #remote map listen port self.real_ip_address=(self.public_ip,self.public_map_port) #................For local listening................... self.ip=str(self.getNetworkIp()) self.port=self.public_map_port #local listen port self.listening_addr=(self.ip,self.port) #................For test in local Lan, if self.unified_port=True,it can be tested in Lan,otherwise it must be tested in WAN if self.unified_port: self.public_ip=self.ip self.public_map_port=self.port self.real_ip_address=self.listening_addr self.open_listener() else: self.open_local_listener() self.ip=self.listening_addr[0] self.port=self.listening_addr[1] #local listen port self.public_ip=self.ip self.public_map_port=self.port self.real_ip_address=self.listening_addr def getNetworkIp(self): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('google.com', 0)) return s.getsockname()[0] def get_real_ip(self): # group = re.compile(u'(?P<ip>\d+\.\d+\.\d+\.\d+)').search(urllib.URLopener().open('http://jsonip.com/').read()).groupdict() # return group['ip'] ip = urllib.urlopen('http://ip.42.pl/raw').read() print ip return ip def connect_server(self): # Try Primary Server if self.connection_server=='': print "Request Primary Server in next 5s, please wait" past=time.time(); self.t=0 while self.t<5: try: self.s = socket.create_connection((CS_Primary_Request_IP, CS_Primary_Request_Port),10) self.connection_state=True self.connection_server='Primary' print "Connected to Primary Server!!!" break except: self.connection_state=False self.t=time.time()-past # Try Secondary server if self.connection_state==False: print "Primary Server fails,.............." print "Request Secondary Server in next 10s,please wait" past=time.time(); self.t=0 while self.t<10: try: self.s = socket.create_connection((CS_Backup_Request_IP, CS_Backup_Request_Port),10) print "Connected to Secondary Server!!!" self.connection_state=True self.connection_server='Secondary' break except: self.connection_state=False self.t=time.time()-past if self.connection_server=='Secondary' and self.username: self.hb_event.set() self.poll_event.set() self.s.shutdown(socket.SHUT_RDWR) self.s.close() # Give user feedback and close the program if self.connection_state==False: print "Secondary Server fails,,.............." time.sleep(1) print "We are sorry for that, you cannot update your userlist currently" past=time.time() self.t=0 while self.t<10: self.t=time.time()-past sys.exit() def send_hb(self): flag=True while True: self.hb_event.wait() if self.connection_state and self.connection_server=='Primary': try: self.hb = socket.create_connection((CS_Primary_Request_IP, CS_Primary_Request_Port)) self.hb_enable=True except: if flag: print "\n................Primary Server May be down........................" flag=False self.hb_enable=False elif self.connection_state and self.connection_server=='Secondary': try: self.hb = socket.create_connection((CS_Backup_Request_IP, CS_Backup_Request_Port)) self.hb_enable=True except: print "\n................Secondary Server May be down........................" self.hb_enable=False if self.hb_enable: try: message=('PyHB',self.username,self.logical_time) self.hb.send(str(message)) self.logical_time=self.logical_time+1 except: print "HB cannot send to server" print "\nHeartbeat Message is sent to '%s' %s" % (str(self.connection_server), str(time.ctime()) ) self.received_hb[self.connection_server]=time.time() self.hb_enable=False # self.hb.shutdown(socket.SHUT_RDWR) self.hb.close() time.sleep(BEAT_PERIOD) def detectLost(self): while True: limit = time.time() - CHECK_TIMEOUT self.detectlost_lock.acquire() servername='' for servername in self.received_hb.keys(): if self.received_hb[servername]<= limit: del self.received_hb[servername] if self.normal_shutdown: print "------------------I gracefully leave the %s Server --------------------" %servername self.normal_shutdown=False else: print "------------------%s Server get lost--------------------" %servername self.connection_state=False self.hb_event.clear() self.connect_server() self.detectlost_lock.release() time.sleep(1) def poll_server(self): t=15 while True: self.poll_event.wait() print "Primary is being polled" if self.connection_state and self.connection_server=='Secondary' : try: self.poll = socket.create_connection((CS_Primary_Request_IP, CS_Primary_Request_Port)) pconn=True except: pconn=False if pconn: print "...................Server state exchange is processing...................." self.poll.shutdown(socket.SHUT_RDWR) self.poll.close() self.normal_shutdown=True self.connection_state=True;self.connection_server='Primary' self.poll_event.clear() time.sleep(t) # if t<3600: # t=t+60 # else: # t=3600 def print_list(self,data): print ".......................User Table is.....................\n" num=1; userlist=ast.literal_eval(data) for i in userlist[1]: print num,':',i[0],':',i[1],':',i[2],'\n' num=num+1 def init_username(self): addr=self.listening_addr; message=('CHB', self.public_ip, self.public_map_port, self.username) try : self.s.sendall(str(message)) except Exception as inst : print type(inst) print inst # print "send to server error" self.init_username_error() infds_c,outfds_c,errfds_c = select.select([self.s,],[],[]) data='' if len(infds_c)!= 0: print '4' try: data=self.s.recv(8192) except Exception as inst : print "receive from server error" print type(inst) print inst self.init_username_error() self.print_list(data) if len(data) != 0: print '2' # self.s.shutdown(socket.SHUT_RDWR) self.s.close() self.hb_event.set() if self.connection_server=="Secondary": self.poll_event.set() xyz=ast.literal_eval(data)# change it to tuple if xyz[0] == 'UT': print '1' self.session_table_lock.acquire() self.music_table_lock.acquire() # contact other clients except myself for ut_item in xyz[1] : key = (ut_item[0], int(ut_item[1])) self.session_table[key] = {'username' : ut_item[2], 'app_start_time' : None, 'logical_clk_time' : None, 'last_recv_time' : time.time()} if key == self.real_ip_address : self.music_table[key] = self.music_info self.session_table[key]['app_start_time'] = self.app_start_time self.music_table_lock.release() self.session_table_lock.release() print "first time multicasting discovery message to clients - this line shouldn't be seen twice" self.multicast_CCD() else : print 'something is wrong 1' else : print 'something is wrong 3' def init_username_error(self) : print "server is down in sending first server discovery" try : self.s.shutdown(socket.SHUT_RDWR) self.s.close() except : print 'shut down connection to server error' self.connect_server() self.init_username() def receive_client(self): while True : peer_socket, peer_address = self.listening_sock.accept() peer_socket.settimeout(10) data = peer_socket.recv(8192 * 16) # print data; #message=Client_Message(None,None,None,None,None) peer_socket.close() try: # with open('pfile', 'wb') as f : # f.write(data) # with open('pfile', 'rb') as f : # message = pickle.load(f) message = pickle.loads(data); print message # print message print 'receive new message of type %s, from client %s' % (message.m_type, message.sender_listening_addr) except Exception as inst: print 'in receive_client', print type(inst) print inst # message.m_type=None # message.sender_listening_addr=None continue self.session_table_lock.acquire() session_table_entry = {}; # check logical_clk_time if message.sender_listening_addr in self.session_table.keys() and message.logical_clk_time <= self.session_table[message.sender_listening_addr]['logical_clk_time'] : self.session_table_lock.release() continue session_table_entry['logical_clk_time'] = message.logical_clk_time # record last_recv_time session_table_entry['last_recv_time'] = time.time() session_table_entry['username'] = message.username session_table_entry['app_start_time'] = message.app_start_time self.session_table[message.sender_listening_addr] = session_table_entry self.session_table_lock.release() if message.m_type == 'CCHB' or message.m_type == 'CCD' : self.music_table_lock.acquire() self.music_table[message.sender_listening_addr] = message.music_info self.music_table_lock.release() print "client discovery | heartbeat message received" print message.m_type, message.sender_listening_addr if message.m_type == 'CCD' : # send hb message back right asway print 'message received is discovery; sending back a client heartbeat message as reply' self.send_C_Music(message.sender_listening_addr, 'CCHB') elif message.m_type == 'LIKE' or message.m_type == 'STREAM' or message.m_type == 'REP' : if message.receiver_app_start_time == self.app_start_time : print 'app_start_times match' if message.m_type == 'LIKE' and message.song_seq_no in self.music_info.keys() : self.music_info_lock.acquire() self.music_info[message.song_seq_no].like = self.music_info[message.song_seq_no].like+1 self.music_info_lock.release() elif message.m_type == 'STREAM' : # print 'stream' self.thread_stream = threading.Thread(target=self.stream_music, args=(message,)) self.thread_stream.start() elif message.m_type == 'REP' : # self.patch_music_table_rep(self.listening_addr, message.song_seq_no, message.sender_listening_addr, message.cache_seq) self.patch_music_table_rep(self.read_ip__address, message.song_seq_no, message.sender_listening_addr, message.cache_seq) self.multicast_C_Music('CCHB') else : print 'app_start_times don\'t match' # print message.receiver_app_start_time, self.app_start_time self.send_C_Music(message.sender_listening_addr, 'CCHB') def stream_music(self, message) : ''' This function handles the streaming request message ''' song_local_seq = message.song_seq_no if song_local_seq in self.file_table.keys() : song_local_path = self.file_table[song_local_seq] print "-----------------------------Stream Info-------------------------------" print "request song_num :",song_local_seq print "request song path :",song_local_path print "send stream to ip :",message.sender_listening_addr[0] print "send stream to port :", message.sender_listening_addr[1] self.player.sender_init(message.sender_listening_addr[0],message.sender_listening_addr[1],song_local_path) else : # FIXME: requested song is not there; should reply with rejection print "Song is not in current peer now" pass pass def dump_table(self): print 'dump tables: ' print '..............................Music Table...........................' for key in self.music_table.keys(): print "..............." ,key for i in self.music_table[key].keys(): print i,":",self.music_table[key][i],'\n' # print self.session_table print '..............................File Table...........................' for key in self.file_table: print key,":",self.file_table[key],'\n' print "..............................Self Music INfo............................" print self.music_info def send_obj(self, addr, obj): try : try: self.send_socket = socket.create_connection(addr, 10) self.is_connected=True except: self.is_connected=False print "+++++++++++++++++++++++Connection Problem++++++++++++++++++++++" if self.is_connected: # with open('pfile', 'wb') as f: # pickle.dump(obj, f) data = pickle.dumps(obj) # with open('pfile', 'rb') as f: # self.send_socket.sendall(f.read()); self.send_socket.sendall(data); except Exception as inst: print type(inst) print inst print "send_obj() exception. addr: %s obj: %s" % (addr, str(obj)) raise def send_request(self, request_type, receiver_key, song_seq_num, cache_seq=-1): self.logical_clk_lock.acquire() self.logical_clk_time += 1 try : self.streaming_addr=receiver_key self.send_obj(receiver_key, Client_Request_Message(request_type, self.real_ip_address, self.username, self.app_start_time, self.logical_clk_time, self.session_table[receiver_key]['app_start_time'], song_seq_num, self.streaming_addr[1], cache_seq)) except Exception as inst: print type(inst) print inst print "send_request() exception" finally: self.logical_clk_lock.release() def send_rep(self, receiver_key, receiver_song_seq_num, cache_seq) : self.send_request('REP', receiver_key, receiver_song_seq_num, cache_seq) def send_like(self, receiver_key, song_seq_num): self.send_request('LIKE', receiver_key, song_seq_num) def send_stream(self, receiver_key, song_seq_num, owner_key, owner_song_seq_num): self.send_request('STREAM', receiver_key, song_seq_num) self.stream_ip=receiver_key[0]; self.stream_port=int(receiver_key[1]); self.stream_song_num=int(song_seq_num) #print "self.stream_ip",self.stream_ip #print "self.stream_port",self.stream_port print ".........................Stream Info..................................." print "request song num :",self.stream_song_num print "receive stream ip :",self.ip print "receive stream port :",self.port self.player.receiver_init(self.ip,self.port, self.stream_song_num, owner_key, owner_song_seq_num) def send_C_Music(self, address, m_type): self.logical_clk_lock.acquire() self.logical_clk_time += 1 try : self.music_info_lock.acquire() # self.music_info = {1: '2'} self.send_obj(address, Client_Music_Message(m_type, self.real_ip_address, self.username, self.app_start_time, self.logical_clk_time, self.music_info)) except Exception as inst: print type(inst) print inst print "send_C_Music() exception addr %s obj: %s" % (address, self.music_info) finally: self.music_info_lock.release() self.logical_clk_lock.release() def multicast_C_Music(self, m_type): print "multicasting music info to clients" self.session_table_lock.acquire() for k in self.session_table.keys() : if k!=self.real_ip_address and k!=0 and k!=None : self.send_C_Music(k, m_type) self.session_table_lock.release() def multicast_CCD(self): self.multicast_C_Music('CCD') def multicast_CCHB(self): self.multicast_C_Music('CCHB') def period_CCHB(self): while True : time.sleep(20) self.multicast_CCHB() def client_liveness_check(self): liveness_threshold = 20 while True: time.sleep(15) self.session_table_lock.acquire() for k in self.session_table.keys() : if k != self.real_ip_address : if(time.time() - self.session_table[k]['last_recv_time'] > liveness_threshold): self.remove_lost_client(k) self.session_table_lock.release() def remove_lost_client(self,key): # Delete client from all the tables self.remove_session_table(key) self.remove_music_table(key) def remove_session_table(self,key): del self.session_table[key] def remove_music_table(self, key): self.music_table_lock.acquire() if key in self.music_table.keys(): del self.music_table[key] self.music_table_lock.release() def close_listen_port(self,port): while True: try: command1="netstat -nltp |grep %s" % port a=os.popen(command1).read() if len(a)!=0: try: command1="kill -9 $(netstat -tlnp|grep %s | awk \'{ print $7 }\' |awk -F \'/\' \'{print $1 }\')" % port os.system(command1) except: pass else: break except: pass def look_up_cache(self, receiver_key, song_seq_num): local_seq = -1 no_rep = True rep = None print 'look_up_cache: receiver_key: %s, song_seq_num: %s' % (receiver_key, song_seq_num) if receiver_key not in self.music_table.keys() or song_seq_num not in self.music_table[receiver_key].keys(): print 'receiver_key: %s, song_seq_num: %s, not in table' % (receiver_key, song_seq_num) return (local_seq, no_rep, rep) print self.music_table print 'in look_up: ', self.music_table[receiver_key][song_seq_num].rep_dict if self.real_ip_address in self.music_table[receiver_key][song_seq_num].rep_dict.keys() : # if self.listening_addr in self.music_table[receiver_key][song_seq_num].rep_dict.keys() : # local_seq = self.music_table[receiver_key][song_seq_num].rep_dict[self.listening_addr] local_seq = self.music_table[receiver_key][song_seq_num].rep_dict[self.real_ip_address] print 'receiver_key: %s, song_seq_num: %s, in table with hit' % (receiver_key, song_seq_num) no_rep = False else : rep_len = len(self.music_table[receiver_key][song_seq_num].rep_dict.items()) if rep_len == 0 : print 'receiver_key: %s, song_seq_num: %s, in table without cache ' % (receiver_key, song_seq_num) no_rep = True else : rep_items = self.music_table[receiver_key][song_seq_num].rep_dict.items() rep_items.append((receiver_key, song_seq_num)) no_rep = False rep = rep_items[random.randint(0, rep_len)] print 'receiver_key: %s, song_seq_num: %s, in table with multiple cache ' % (receiver_key, song_seq_num) return (local_seq, no_rep, rep) def try_play(self, owner_key, owner_song_seq_num) : if self.player.is_playing : print "It is playing now" self.player.pause() self.player.stop() if self.real_ip_address == owner_key and owner_song_seq_num in self.file_table.keys(): # if self.listening_addr == owner_key and owner_song_seq_num in self.file_table.keys(): print 'playing from local repo' self.player.play(self.file_table[owner_song_seq_num]) else : local_seq, no_rep, rep = self.look_up_cache(owner_key, owner_song_seq_num) if no_rep == True : print 'no cache; send request to owner: %s %s' % (owner_key, owner_song_seq_num) self.send_stream(owner_key, owner_song_seq_num, owner_key, owner_song_seq_num) elif rep != None : print 'multiple cache; send request to one of them: %s %s, owner: %s %s' % (rep[0], rep[1], owner_key, owner_song_seq_num) self.send_stream(rep[0], rep[1], owner_key, owner_song_seq_num) else : print 'play from local cache' self.player.play(self.file_table[local_seq]) def open_listener(self): self.listening_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # self.close_listen_port(self.port) # self.close_listen_port(int(self.port+11)) # self.close_listen_port(int(self.port+12)) print "self.listening_addr ",self.listening_addr try: self.listening_sock.bind(self.listening_addr) except: pass #self.listening_sock.bind(self.listening_addr) self.listening_sock.listen(5) def open_local_listener(self): self.listening_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.listening_addr = (socket.gethostbyname(socket.gethostname()), 0) self.listening_addr = (str(self.getNetworkIp()), 0) self.listening_sock.bind(self.listening_addr) self.listening_addr = self.listening_sock.getsockname() self.listening_sock.listen(5) return self.listening_addr def add_song(self,filepath): repo_path = os.path.abspath(self.repo_path) # FIXME: path should be changed later #check if song with same name exists self.music_info_lock.acquire() if(self.music_info_object.check_song_exists(self.music_info,filepath)==False): # copy the file from current location to repo folder shutil.copy(filepath,repo_path) self.music_counter[0] +=1; self.music_info_object.read_song(self.music_info,self.file_table,self.music_counter[0], filepath) self.music_info_lock.release() else: self.music_info_lock.release() print 'duplicate song already exists in library' def add_cache(self, cache_path) : self.music_info_lock.acquire() self.music_counter[0] +=1; seq = self.music_counter[0] self.music_info_object.add_cache(self.file_table,self.music_counter[0], cache_path) self.music_info_lock.release() return seq def add_song_server(self, filepath): self.music_info_lock.acquire() self.music_counter[0] +=1; self.music_info_object.read_song(self.music_info,self.file_table,self.music_counter[0], filepath) self.music_info_lock.release() def remove_song(self,filepath) : #check if song exists self.music_info_lock.acquire() #Remove song from song_dict/music_info self.music_info_object.remove_song(self.music_info,self.file_table,filepath) self.music_info_lock.release() def top_ten(self): song_list=[] # print '-----------------------------------top 10--------------------------------------' for user,song_info in self.music_table.items(): for seq,song in song_info.items(): user_info=(user,seq) song_list.append((user_info,song)) # print 'song: ', # print song sorted_list = sorted(song_list,key=lambda x:x[1].like,reverse=True) # print sorted_list return sorted_list def patch_music_table_rep(self, holder_key, holder_song_seq_num, rep_listening_addr, cache_seq_num) : self.music_table_lock.acquire() if holder_key in self.music_table.keys() and holder_song_seq_num in self.music_table[holder_key].keys() : self.music_table[holder_key][holder_song_seq_num].add_rep(rep_listening_addr, cache_seq_num) print 'in patch: ', self.music_table[holder_key][holder_song_seq_num].rep_dict print self.music_table else : print 'not in table (%s, %s)' % (holder_key, holder_song_seq_num) self.music_table_lock.release() def run(self): while (True) : command_str = raw_input("> "); if command_str == '' : continue command = command_str.split(' '); if command[0] == 'user' : self.username = command[1] print "the self.listening_address is ,",self.listening_addr self.init_username() elif command[0] == 'SD' : self.send_SD() elif command[0] == 'listen' : self.open_listener(); elif command[0] == 'like' : self.send_like((command[1], int(command[2])), int(command[3])) elif command[0] == 'stream' : if self.player.is_playing: print "It is playing now" self.player.pause() self.player.stop() else: print "I am lucky" self.send_stream((command[1], int(command[2])), int(command[3])) elif command[0] == 'add': self.add_song(command[1]) elif command[0]=='remove': self.remove_song(command[1]) elif command[0] == 'dump' : self.dump_table() elif command[0] == 'q' : sys.exit() elif command[0]=='pause': self.player.pause() elif command[0]=='resume': self.player.resume() elif command[0]=='stop': self.player.stop() elif command[0]=='replay': if not self.player.check_cache_dic(self.stream_song_num): self.send_stream((self.stream_ip,self.stream_port),self.stream_song_num) elif command[0]=='play': song_playing_flag=False song_num=int(command[1]) for key in self.file_table.keys(): if key==song_num: songpath=self.file_table[key] self.player.play(songpath) song_playing_flag=True if song_playing_flag==False: cachepath=self.player.traverse_cache_dic(song_num) if len(cachepath)!=0: self.player.play(cachepath) else: print "Sorry there is no that song in the repo" else : print "command not recognized"