//Transmit a packet and handle retransmission attempts int transmit_packet(minisocket_t socket, network_address_t dst_addr, int dst_port, short incr_seq, char message_type, int data_len, char* data, minisocket_error* error) { mini_header_reliable_t newReliableHeader; void *alarmId; int sendSucessful; network_address_t my_addr; int success = 0; int connected = 0; if (message_type == MSG_ACK) connected = 1; network_get_my_address(my_addr); if (socket == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (socket->status == TCP_PORT_CLOSING) { *error = SOCKET_SENDERROR; return -1; } newReliableHeader = create_reliable_header(my_addr, socket->port_number, dst_addr, dst_port, message_type, socket->seq_number, socket->ack_number); socket->timeout = 100; while(socket->timeout <= 6400) { printf("sending packet to %d, seq=%d, ack=%d, type=%d\n", dst_port, socket->seq_number, socket->ack_number, message_type); sendSucessful = network_send_pkt(dst_addr, sizeof(struct mini_header_reliable), (char*) newReliableHeader, data_len, (char*) data); if (sendSucessful == -1) { socket->timeout *= 2; continue; } alarmId = register_alarm(socket->timeout, &wake_up_semaphore, socket); if (message_type == MSG_SYN) { socket->waiting = TCP_PORT_WAITING_SYNACK; semaphore_P(socket->wait_for_ack_semaphore); } else if (!connected) { socket->waiting = TCP_PORT_WAITING_ACK; semaphore_P(socket->wait_for_ack_semaphore); } else { socket->waiting = TCP_PORT_WAITING_NONE; } semaphore_P(socket->mutex); if (socket->waiting == TCP_PORT_WAITING_NONE) { /* I think incr_seq belongs before the _P on wait_for_ack, but that breaks it for now :/ if (incr_seq) { semaphore_P(socket->mutex); socket->seq_number++; semaphore_V(socket->mutex); } */ if (message_type == MSG_SYN) { //we got a synack back, so we need to make sure to send an ack to the server newReliableHeader = create_reliable_header(my_addr, socket->port_number, dst_addr, dst_port, MSG_ACK, socket->seq_number, socket->ack_number); socket->timeout = 100; message_type = MSG_ACK; connected = 1; semaphore_V(socket->mutex); continue; } deregister_alarm(alarmId); success = 1; semaphore_V(socket->mutex); break; } else { if (socket->status == TCP_PORT_UNABLE_TO_CONNECT) { deregister_alarm(alarmId); success = 0; semaphore_V(socket->mutex); break; } socket->timeout *= 2; semaphore_V(socket->mutex); } } semaphore_P(socket->mutex); socket->timeout = 100; if (success == 0) { socket->waiting = TCP_PORT_WAITING_NONE; *error = SOCKET_NOSERVER; } semaphore_V(socket->mutex); *error = SOCKET_NOERROR; free(newReliableHeader); return 0; }
/* Transmit a packet and handle retransmissions */ int transmit_packet(minisocket_t socket, network_address_t dst_addr, int dst_port, short incr_seq, char message_type, int data_len, char* data, minisocket_error* error) { mini_header_reliable_t header; int alarm_id; short success = 0; network_address_t my_addr; int send_ret; interrupt_level_t prev_level; network_get_my_address(my_addr); // Return if the socket is invalid if (socket == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } // Don't allow new packets to be transmitted if the port's closing if (socket->status == TCP_PORT_CLOSING) { *error = SOCKET_SENDERROR; return -1; } // Increment the seq# if needed if (incr_seq == 1) { socket->seq_num++; } // Create the header header = create_reliable_header(my_addr, socket->port_number, dst_addr, dst_port, message_type, socket->seq_num, socket->ack_num); // Can retransmit for each timeout up to 6400 (100 + 200 + 400 + ... + 6400 = 12.7s) socket->timeout = 100; while (socket->timeout <= 6400) { // send the packet send_ret = network_send_pkt(dst_addr, sizeof(struct mini_header_reliable), (char*) header, data_len, (char*) data); // Check if there was an error sending if (send_ret == -1) { // Could've been a fluke, let it keep going continue; } // Seq # is count of retransmissable packets... if dont increment it, // then this isn't retransmissable, so end if (incr_seq == 0) { free(header); return 0; } // Set an alarm to wake us up after timeout alarm_id = register_alarm(socket->timeout, &wake_up_sem, socket); // Specify the type of packet we're waiting for if (message_type == MSG_SYN) { socket->waiting = TCP_PORT_WAITING_SYNACK; } else { socket->waiting = TCP_PORT_WAITING_ACK; } // Sleep on semaphore prev_level = set_interrupt_level(DISABLED); semaphore_P(socket->wait_for_ack_sem); set_interrupt_level(prev_level); // Check if we got the packet when we've awoken if (socket->waiting == TCP_PORT_WAITING_NONE) { // Yes, we did - delete the alarm and break out of the loop deregister_alarm(alarm_id); success = 1; break; } else { if (socket->status == TCP_PORT_COULDNT_CONNECT) { // This means the client sent a FIN while we were trying to connect success = 0; deregister_alarm(alarm_id); break; } // No, we didn't - double the timeout and repeat if we can socket->timeout *= 2; } } // So the IH knows the sequence is done (see comment block in network_common:170) // There is a small chance that the code around network_common:170 won't catch // this in time and won't send one last packet - that is okay, as Prof. Sirer // mentioned sending that packet isn't even necessary in the first place. socket->timeout = 100; // Free the header as it's no longer needed free(header); // If we weren't successful... if (success == 0) { // Decrement the seq# if we incremented it earlier if (incr_seq == 1) { socket->seq_num--; } // Reset the socket's waiting status socket->waiting = TCP_PORT_WAITING_NONE; // Set the error & return *error = SOCKET_NOSERVER; return -1; } return 0; }