void rudp_peer_reset(struct rudp_peer *peer) { struct rudp_packet_chain *pc, *tmp; rudp_list_for_each_safe(struct rudp_packet_chain*, pc, tmp, &peer->sendq, chain_item) { rudp_list_remove(&pc->chain_item); rudp_packet_chain_free(peer->rudp, pc); } if ( peer->scheduled ) ela_remove(peer->rudp->el, peer->service_source); peer->scheduled = 0; peer->abs_timeout_deadline = rudp_timestamp() + DROP_TIMEOUT; peer->in_seq_reliable = (uint16_t)-1; peer->in_seq_unreliable = 0; peer->out_seq_reliable = rudp_random(peer->rudp); peer->out_seq_unreliable = 0; peer->out_seq_acked = peer->out_seq_reliable - 1; peer->state = PEER_NEW; peer->last_out_time = rudp_timestamp(); peer->srtt = 100; peer->rttvar = peer->srtt / 2; peer->rto = MAX_RTO; peer->must_ack = 0; peer->sendto_err = 0; }
static void peer_service_schedule(struct rudp_peer *peer) { // If nothing in sendq: reschedule service for later rudp_time_t delta = ACTION_TIMEOUT; // just abuse for_each to get head, if it exists struct rudp_packet_chain *head; rudp_list_for_each(struct rudp_packet_chain*, head, &peer->sendq, chain_item) { struct rudp_packet_header *header = &head->packet->header; if ( header->opt & RUDP_OPT_RETRANSMITTED ) // already transmitted head, wait for rto delta = rudp_timestamp() - peer->last_out_time + peer->rto; else // transmit asap delta = 0; // We dont really want to iterate after head break; } rudp_time_t to_delta = peer->abs_timeout_deadline - rudp_timestamp(); if ( to_delta < delta ) delta = to_delta; if ( delta <= 0 ) delta = 1; rudp_log_printf(peer->rudp, RUDP_LOG_DEBUG, "%s:%d Idle, service scheduled for %d\n", __FUNCTION__, __LINE__, (int)delta); struct timeval tv; rudp_timestamp_to_timeval(&tv, delta); ela_set_timeout(peer->rudp->el, peer->service_source, &tv, ELA_EVENT_ONCE); ela_add(peer->rudp->el, peer->service_source); peer->scheduled = 1; }
static rudp_error_t peer_send_raw( struct rudp_peer *peer, const void *data, size_t len) { peer->sendto_err = rudp_endpoint_send( peer->endpoint, &peer->address, data, len); peer->last_out_time = rudp_timestamp(); return peer->sendto_err; }
static void peer_handle_pong( struct rudp_peer *peer, const struct rudp_packet_chain *pc) { rudp_time_t orig, delta; memcpy(&orig, pc->packet->data.data, sizeof(orig)); delta = rudp_timestamp() - orig; peer_update_rtt(peer, delta); }
static void peer_ping(struct rudp_peer *peer) { struct rudp_packet_chain *pc = rudp_packet_chain_alloc( peer->rudp, sizeof(struct rudp_packet_header) + sizeof(rudp_time_t) ); struct rudp_packet_data *data = &pc->packet->data; rudp_log_printf(peer->rudp, RUDP_LOG_DEBUG, "%s pushing PING\n", __FUNCTION__); rudp_time_t now = rudp_timestamp(); data->header.command = RUDP_CMD_PING; memcpy(data->data, &now, sizeof(now)); rudp_peer_send_reliable(peer, pc); }
rudp_error_t rudp_init( struct rudp *rudp, struct ela_el *el, const struct rudp_handler *handler) { rudp->handler = handler; rudp->el = el; rudp_list_init(&rudp->free_packet_list); rudp->free_packets = 0; rudp->allocated_packets = 0; rudp->seed = rudp_timestamp(); rudp_random(rudp); rudp_random(rudp); rudp_random(rudp); return 0; }
rudp_error_t rudp_peer_send_close_noqueue(struct rudp_peer *peer) { struct rudp_packet_header header = { .command = RUDP_CMD_CLOSE, .opt = 0, }; header.opt = 0; header.reliable = htons(peer->out_seq_reliable); header.unreliable = htons(++(peer->out_seq_unreliable)); rudp_log_printf(peer->rudp, RUDP_LOG_IO, ">>> outgoing noqueue %s (%d) %04x:%04x\n", rudp_command_name(header.command), ntohs(header.reliable), header.reliable, ntohs(header.unreliable)); return peer_send_raw(peer, &header, sizeof(header)); } /* Worker functions */ static void peer_send_queue(struct rudp_peer *peer) { struct rudp_packet_chain *pc, *tmp; rudp_list_for_each_safe(struct rudp_packet_chain*, pc, tmp, &peer->sendq, chain_item) { struct rudp_packet_header *header = &pc->packet->header; if ( peer->must_ack ) { header->opt |= RUDP_OPT_ACK; header->reliable_ack = htons(peer->in_seq_reliable); // peer->must_ack = 0; } rudp_log_printf(peer->rudp, RUDP_LOG_IO, ">>>>>> %ssend %sreliable %s %04x:%04x %s %04x\n", header->opt & RUDP_OPT_RETRANSMITTED ? "RE" : "", header->opt & RUDP_OPT_RELIABLE ? "" : "un", rudp_command_name(pc->packet->header.command), ntohs(pc->packet->header.reliable), ntohs(pc->packet->header.unreliable), header->opt & RUDP_OPT_ACK ? "ack" : "noack", ntohs(pc->packet->header.reliable_ack)); peer_send_raw(peer, header, pc->len); if ( (header->opt & RUDP_OPT_RELIABLE) && (header->opt & RUDP_OPT_RETRANSMITTED) ) { peer_rto_backoff(peer); break; } if ( header->opt & RUDP_OPT_RELIABLE ) { header->opt |= RUDP_OPT_RETRANSMITTED; // break; } else { rudp_list_remove(&pc->chain_item); rudp_packet_chain_free(peer->rudp, pc); } } } /* Two reasons may bring us here: - There are some (un)reliable items in the send queue, either - we are retransmitting - we just enqueued something and we need to send - There is nothing in the send queue and we want to ensure the peer is still up */ static void peer_service(struct rudp_peer *peer) { peer->scheduled = 0; if ( peer->abs_timeout_deadline < rudp_timestamp() ) { peer->handler->dropped(peer); return; } if ( rudp_list_empty(&peer->sendq) ) { /* Nothing was in the send queue, so we may be in a timeout situation. Handle retries and final timeout. */ rudp_time_t out_delta = rudp_timestamp() - peer->last_out_time; if ( out_delta > ACTION_TIMEOUT ) peer_ping(peer); } peer_send_queue(peer); peer_service_schedule(peer); }
/* - socket watcher - endpoint packet reader - server packet handler - peer packet handler <=== */ rudp_error_t rudp_peer_incoming_packet( struct rudp_peer *peer, struct rudp_packet_chain *pc) { const struct rudp_packet_header *header = &pc->packet->header; rudp_log_printf(peer->rudp, RUDP_LOG_IO, "<<< incoming [%d] %s %s (%d) %04x:%04x\n", peer->state, (header->opt & RUDP_OPT_RELIABLE) ? "reliable" : "unreliable", rudp_command_name(header->command), header->command, ntohs(header->reliable), ntohs(header->unreliable)); if ( header->opt & RUDP_OPT_ACK ) { rudp_log_printf(peer->rudp, RUDP_LOG_IO, " has ACK flag, %04x\n", (int)ntohs(header->reliable_ack)); int broken = peer_handle_ack(peer, ntohs(header->reliable_ack)); if ( broken ) { rudp_log_printf(peer->rudp, RUDP_LOG_WARN, " broken ACK flag, ignoring packet\n"); return EINVAL; } } enum packet_state state; if ( header->opt & RUDP_OPT_RELIABLE ) state = peer_analyse_reliable(peer, ntohs(header->reliable)); else state = peer_analyse_unreliable(peer, ntohs(header->reliable), ntohs(header->unreliable)); switch ( state ) { case UNSEQUENCED: if (peer->state == PEER_NEW && header->command == RUDP_CMD_CONN_REQ) { // Server side, handling new client peer_handle_connreq(peer, header); peer->in_seq_reliable = ntohs(header->reliable); peer->state = PEER_RUN; } else if (peer->state == PEER_CONNECTING && header->command == RUDP_CMD_CONN_RSP) { // Client side, handling new server peer->in_seq_reliable = ntohs(header->reliable); peer_handle_ack(peer, ntohs(header->reliable_ack)); peer->state = PEER_RUN; } else { rudp_log_printf(peer->rudp, RUDP_LOG_WARN, " unsequenced packet in state %d, ignored\n", peer->state); } break; // return RUDP_EINVAL; case RETRANSMITTED: peer->abs_timeout_deadline = rudp_timestamp() + DROP_TIMEOUT; break; case SEQUENCED: peer->abs_timeout_deadline = rudp_timestamp() + DROP_TIMEOUT; switch ( header->command ) { case RUDP_CMD_CLOSE: peer->state = PEER_DEAD; peer->handler->dropped(peer); rudp_log_printf(peer->rudp, RUDP_LOG_INFO, " peer dropped\n"); return 0; break; case RUDP_CMD_PING: if ( peer->state == PEER_RUN ) { rudp_log_printf(peer->rudp, RUDP_LOG_DEBUG, " ping\n"); peer_handle_ping(peer, pc); } else { rudp_log_printf(peer->rudp, RUDP_LOG_WARN, " ping while not running\n"); } break; case RUDP_CMD_PONG: if ( peer->state == PEER_RUN ) { rudp_log_printf(peer->rudp, RUDP_LOG_DEBUG, " pong\n"); peer_handle_pong(peer, pc); } else { rudp_log_printf(peer->rudp, RUDP_LOG_WARN, " pong while not running\n"); } break; case RUDP_CMD_NOOP: case RUDP_CMD_CONN_REQ: case RUDP_CMD_CONN_RSP: break; default: if ( peer->state != PEER_RUN ) { rudp_log_printf(peer->rudp, RUDP_LOG_WARN, " user payload while not running\n"); break; } if ( header->command >= RUDP_CMD_APP ) peer->handler->handle_packet(peer, pc); } } if ( header->opt & RUDP_OPT_RELIABLE ) { rudp_log_printf(peer->rudp, RUDP_LOG_DEBUG, " reliable packet, posting ack\n"); peer_post_ack(peer); } peer_service_schedule(peer); return 0; }
uint64_t rudp_random() { srand(rudp_timestamp()); return rand(); }