def process_ack(self, ack_seq, ack_bits): #from net.settings import IS_CLIENT #if not IS_CLIENT: # ipdb.set_trace() _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("pre process_ack pending acks ack_seq %d bits %x : %s" % (ack_seq, ack_bits, self.pendingAckQueue)) if is_seq_more_recent(ack_seq, self.latest_ack_seq): self.latest_ack_seq = ack_seq if self.pendingAckQueue: new_queue = ReliableUDPClientConnection.ReliabilitySystem.PacketQueue() for ack_info in self.pendingAckQueue: acked = False #if ack_info.seq == ack_seq and bit_index_for_sequence(ack_info.seq, ack_seq): # acked = True #el if not is_seq_more_recent(ack_info.seq, ack_seq) or ack_info.seq == ack_seq: bit_index = bit_index_for_sequence(ack_info.seq, ack_seq) if (bit_index <= 31): acked = (ack_bits >> bit_index) & 1 if acked: _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("PROCESSED process_ack for packet %d" % ack_info.seq) self.rtt += (ack_info.time - self.rtt) * 0.1 self.ackedQueue.insert_sorted(ack_info) self.acks.append(ack_info.seq) self.ackd_packets += 1 else: new_queue.append(ack_info) self.pendingAckQueue = new_queue _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("process_ack pending acks: %s" % self.pendingAckQueue)
def packet_received(self, seq, packet): if seq in self.receivedQueue: log.fields(conn=self.conn_info).warning("received packet that was already received, seq %d" % (seq)) #ipdb.set_trace() return False #TODO: not sure if need to include the packet data info = ReliableUDPClientConnection.ReliabilitySystem.PacketData(seq, 0.0, None) self.receivedQueue.append(info) if is_seq_more_recent(seq, self.remote_seq): #remote_msg_seq > self.remote_seq: self.remote_seq = seq self.last_gen_ack = -1 self.cached_ack_bits = -1 return True
def process_datagram(self, dgram): #remote_msg_seq, msg_len, dgram = ReliableHeader.unpack(dgram) time_received = network_time(self.network_epoch) crc_successful = ReliableHeader.check_crc(dgram) if not crc_successful: if _DEBUG_RELIABLE_UDP: header, dgram = ReliableHeader.unpack(dgram) header, dgram = ReliableHeader.unpack(dgram) remote_msg_seq = header.seq msg_len = header.msg_len multi_seq_id = header.multi_seq_id _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).info("FAILED CRC (possibly false header data -- remote_seq %d, msg_len %d ts: %f, rtt: %f" % (remote_msg_seq, msg_len, header.timestamp, self.reliability.rtt)) return # skip reliability system etc so that packet will be resent by sender header, dgram = ReliableHeader.unpack(dgram) remote_msg_seq = header.seq msg_len = header.msg_len multi_seq_id = header.multi_seq_id self.reliability.packet_received(header.seq, dgram) assert(header.timestamp >= 0.0) time_diff = time_received - (header.timestamp + self.reliability.rtt) _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("recvd dgram, remote_seq %d, msg_len %d ts: %f, rtt: %f timediff: %f" % (remote_msg_seq, msg_len, header.timestamp, self.reliability.rtt, time_diff)) if __debug__ and ((self.addr, self.port) != (dgram.addr)): ipdb.set_trace() self.reliability.process_ack(header.ack_seq, header.ack_bits) if (msg_len > ReliableHeader.max_usr_len or msg_len < 0): _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("processing as multipacket multipacketid: %d", multi_seq_id) processed_msg = False for pending in self.pending_multipackets: if pending.needs_msg(multi_seq_id, remote_msg_seq, msg_len, dgram): processed_msg = True #log.fields(conn=self.conn_info).debug("adding to pending multipackets") if pending.is_complete(): #ipdb.set_trace() complete_msg = pending.get_msg() super(ReliableUDPClientConnection, self).process_datagram(complete_msg) self.pending_multipackets.remove(pending) break if not processed_msg: #log.fields(conn=self.conn_info).debug("new pending multipackets") pending_multipacket = PendingMultiPacketMsg(multi_seq_id, remote_msg_seq, msg_len, dgram) self.pending_multipackets.append(pending_multipacket) else: if len(dgram) > 0: # if length zero then it was purely an ack packet super(ReliableUDPClientConnection, self).process_datagram(dgram)
def update(self, dt, rtt, percent_unacked): if self.mode == FlowControl.Mode.Good: if rtt > FlowControl.RTT_THRESHOLD or percent_unacked > FlowControl.UNACKED_THRESHOLD_END: self.mode = FlowControl.Mode.Bad _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).info("flow control: dropping to bad mode") if self.good_conditions_time < 10.0 and self.penalty_time < 60.0: self.penalty_time *= 2.0 if self.penalty_time > 60.0: self.penalty_time = 60.0 self.good_conditions_time = 0.0 self.penalty_reduction_accumulator = 0.0 return self.good_conditions_time += dt self.penalty_reduction_accumulator += dt if self.penalty_reduction_accumulator > 10.0 and self.penalty_time > 1.0: self.penalty_time /= 2.0 if self.penalty_time < 1.0: self.penalty_time = 1.0 _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("flow control: penalty time reduced to %f" % self.penalty_time) self.penalty_accumulator = 0.0 elif self.mode == FlowControl.Mode.Bad: if rtt <= FlowControl.RTT_THRESHOLD and percent_unacked < FlowControl.UNACKED_THRESHOLD_START: self.good_conditions_time += dt else: self.good_conditions_time = 0.0 if self.good_conditions_time > self.penalty_time: _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).info("flow control: upgrading to good mode") self.good_conditions_time = 0.0 self.penalty_reduction_accumulator = 0.0 self.mode = FlowControl.Mode.Good return else: assert(False)
def update_queues(self): while self.sentQueue and (self.sentQueue[0].time > (self.rtt_max + self.EPSILON) ): sent = self.sentQueue.pop(0) _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("SENTQUEUE: removing seq %d" % sent.seq) if self.receivedQueue: last_seq = self.receivedQueue[-1].seq if last_seq >= 34: min_seq = last_seq - 34 else: min_seq = self.max_seq - (34 - last_seq) while self.receivedQueue and not is_seq_more_recent(self.receivedQueue[-1].seq, min_seq): recd = self.receivedQueue.pop(0) _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("RECDQUEUE: removing seq %d" % recd.seq) while self.ackedQueue and self.ackedQueue[-1].time > (self.rtt_max * 2 - self.EPSILON): ackd = self.ackedQueue.pop(0) _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("ACKDQUEUE: removing seq %d" % ackd.seq) while self.pendingAckQueue and self.pendingAckQueue[-1].time > (self.rtt_max + self.EPSILON): packd = self.pendingAckQueue.pop(0) _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("PACKDQUEUE: removing seq %d" % packd.seq) self.lost_packets += 1
def _internal_queue_outgoing(self, header, msg_data, priority): #outgoing_dgram, priority): self.last_msg_dt = 0.0 header.seq = self.reliability.local_seq header.ack_seq = self.reliability.remote_seq header.ack_bits = self.reliability.generate_ack_bits(header.ack_seq) header.timestamp = network_time(self.network_epoch) _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("_internal_queue_outgoing, sending msg seq %d multi_seq %d msg_len %d network time %f" % (header.seq, header.multi_seq_id, header.msg_len, header.timestamp)) full_data = header.to_data(msg_data) outgoing_dgram = Datagram(full_data, (self.addr, self.port)) self.reliability.packet_sent(self.reliability.local_seq, outgoing_dgram) #log.fields(conn=self.conn_info).debug("_internal_queue_outgoing, presend count %d" % len(self.parent.outgoing)) #from net.settings import IS_CLIENT #if not IS_CLIENT: # ipdb.set_trace() # - calling parent queue outgoing super(ReliableUDPClientConnection, self).queue_outgoing(outgoing_dgram, priority) #log.fields(conn=self.conn_info).debug("_internal_queue_outgoing, postsend count %d" % len(self.parent.outgoing)) self.set_writable(True)
def queue_outgoing(self, msg, priority=5, reliable_header = None): if isinstance(msg, Datagram): dgram = msg else: dgram = Datagram(msg, (self.addr, self.port)) #break our datagram into multiple if larger than the allowed size total_bytes = len(dgram) #TODO: check this is getting the right length #assert(total_bytes <= ReliableHeader.max_msg_len) curr_start_idx = 0 curr_end_idx = 0 curr_len = 0 packet_count = 0 multi_packet_seq = -1 max_msg_bytes = ReliableHeader.max_usr_len if total_bytes > ReliableHeader.max_usr_len: multi_packet_seq = self.multi_packet_seq self.multi_packet_seq = seq_add(self.multi_packet_seq, 1) max_msg_bytes = ReliableHeader.max_multi_usr_len if _DEBUG_RELIABLE_UDP and __debug__: num_packets = ReliableHeader.calc_num_packets(total_bytes) log.fields(conn=self.conn_info).info("gen header multi_seq: %d msg_len %d" % (multi_packet_seq, num_packets)) while True: #do while curr_end_idx < total_bytes: curr_len = total_bytes - curr_end_idx if (curr_len > max_msg_bytes): curr_len = max_msg_bytes _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("curr len %d" % curr_len) curr_end_idx = curr_start_idx + curr_len msg_bytes = dgram[curr_start_idx:curr_end_idx] if packet_count > 0: msg_len_field = -packet_count else: msg_len_field = total_bytes _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).debug("gen header multi_seq: %d msg_len %d" % (multi_packet_seq, msg_len_field)) #ack_seq = self.reliability.remote_seq #header = ReliableHeader() #header_bytes = ReliableHeader.generate_header(self.reliability.local_seq, msg_len_field, ack_seq, self.reliability.generate_ack_bits(ack_seq), multi_packet_seq) header = ReliableHeader(-1, msg_len_field, -1, -1, multi_packet_seq) #self.reliability.local_seq = seq_add(self.reliability.local_seq, 1) #self.local_seq + 1 #assert(len(header_bytes + msg_bytes) <= ReliableHeader.max_msg_len) #outgoing_dgram = Datagram(header_bytes + msg_bytes, dgram.addr) #log.fields(conn=self.conn_info).debug("dgram: %s", outgoing_dgram) curr_start_idx = curr_end_idx packet_count = packet_count + 1 #self.reliability.packet_sent(curr_packet_seq, outgoing_dgram) #super(ReliableUDPClientConnection, self).queue_outgoing(outgoing_dgram, priority) #self._internal_queue_outgoing(outgoing_dgram, priority) #self._internal_queue_msg(outgoing_dgram, priority) #self.unsent_queue.put(outgoing_dgram) #self.unsent_queue.inp.append(outgoing_dgram) self._internal_queue_msg(header, msg_bytes) #DO WHILE if curr_end_idx >= total_bytes: break
def update(self): import datetime #__epoce #import time #send_delay = 0.0 #send_accumulator = 0.0 prev_time = datetime.datetime.utcnow() #time.clock() send_rate = self.flow_control.send_rate() while True: #TODO: debug timing slower for now #curr_max_wait_time = ReliableUDPConnection.MAX_TIME_BETWEEN_ACKS #curr_max_wait_time = (1.0 / send_rate) * 4.0 #if self.send_delay > 0.0 and curr_max_wait_time > self.send_delay: # diesel.sleep(self.send_delay) # can't send until this delay is completed # curr_max_wait_time -= self.send_delay #NOTE: assuming send delay was approx what we said to sleep for. # if curr_max_wait_time < 0.0: # curr_max_wait_time = 0.0 # #ipdb.set_trace() # self.send_delay = 0.0 #print curr_max_wait_time #evt, data = diesel.first(sleep=curr_max_wait_time, waits = [self.pending_resends, self.unsent_queue]) evt, data = diesel.first(waits = [self.pending_resends, self.unsent_queue]) #print "send_event wait" if not self.send_event.is_set: self.waiting_to_send = True self.send_event.wait() self.send_delay = 0.0 self.waiting_to_send = False #print "send_event wait DONE" curr_time = datetime.datetime.utcnow() #time.clock() dt = (curr_time - prev_time).total_seconds() prev_time = curr_time self.reliability.update(dt) #update latest acks etc. self.flow_control.update(dt, self.reliability.rtt, len(self.reliability.pendingAckQueue)/32.0 ) next_msg = None priority = None if evt == self.pending_resends: #print "sending a resend, remaining: %d" % len(self.pending_resends.inp) header, next_msg = data elif evt == self.unsent_queue: header, next_msg = data #print "sending unsent msg, remaining %d" % len(self.unsent_queue.inp) missing_msgs = self.reliability.process_missing_msgs() for msg in missing_msgs: #ipdb.set_trace() data_dgram = Datagram(msg.data, self.addr) header, msgdata = ReliableHeader.unpack(data_dgram) prev_seq = header.seq #next_seq = self.reliability.local_seq #header.seq = next_seq #self.reliability.local_seq = seq_add(next_seq, 1) #log.fields(conn=self.conn_info).info("resending unacked msg, prev seq: %d, new seq %d len %d" % (prev_seq, next_seq, header.msg_len)) _DEBUG_RELIABLE_UDP and log.fields(conn=self.conn_info).info("resending unacked msg, prev seq: %d, len %d" % (prev_seq, header.msg_len)) #dgram = header.to_data() + msgdata #self._internal_queue_outgoing(dgram, 1) #self._internal_queue_msg(dgram, 1) #self.pending_resends.put(dgram) self.pending_resends.put((header, msgdata)) #NOTE: handled above #if self.reliability.last_msg_dt > ReliableUDPClientConnection.ReliabilitySystem.MAX_ACK_TIME: # self.queue_outgoing("") #self._internal_queue_outgoing(next_msg, priority) self._internal_queue_outgoing(header, next_msg, priority) per_msg_rate = 1.0 / send_rate self.send_accumulator -= per_msg_rate send_rate = self.flow_control.send_rate() self.send_delay = 0.0 if self.send_accumulator < per_msg_rate: self.send_delay = -(self.send_accumulator - per_msg_rate) #ipdb.set_trace() assert(self.send_delay > 0.0) #print "send_event clear" self.send_event.clear() ipdb.set_trace()