/* *************************************************** * Function: handle_app_data * *************************************************** * The application has new data to be sent out. First make sure that * there is some space in the window. Get maximum amount of data from * the application, added it to the sender's buffer, and send it across * the network. */ static void handle_app_data(mysocket_t sd, context_t *ctx) { size_t bytes_in_transit = ctx->nxt_seq_num - ctx->seq_base; size_t win_space = ctx->recv_win - bytes_in_transit; if (win_space>0){ char *pkt = (char *)calloc(1,(TH_MIN_OFFSET*sizeof(uint32_t)) + STCP_MSS); struct tcphdr* hdr = (struct tcphdr*)pkt; hdr->th_off = TH_MIN_OFFSET; hdr->th_seq = ctx->nxt_seq_num; hdr->th_flags = 0; hdr->th_win = RWIN; /* Pick the minimum between space in the window and MSS */ size_t data_max = MIN(STCP_MSS, win_space); size_t len = stcp_app_recv(sd, pkt + TCP_DATA_START(hdr), data_max); ctx->nxt_seq_num += len; /*add len to obtain nxt_seq_num to be used */ add_to_send_buffer(ctx, pkt, (TH_MIN_OFFSET*sizeof(uint32_t)) + len); our_dprintf("Sending packet with seq: %d of size: %d\n", hdr->th_seq,(TH_MIN_OFFSET*sizeof(uint32_t)) + len); network_send(sd, pkt, (TH_MIN_OFFSET*sizeof(uint32_t)) + len); free(pkt); } else { our_dprintf("."); } }
int handle_cstate_est_send(mysocket_t sd, context_t * ctx){ //our_dprintf("*IN EST SEND\n"); size_t eff_wind = calc_eff_window(ctx); size_t max_to_send = MIN(eff_wind, STCP_MSS); if (max_to_send == 0) { //our_dprintf("window too small to send min(%u, %u) \n", eff_wind, STCP_MSS); return 0; } //our_dprintf("max to send %u\n", max_to_send); /* receive data from app */ size_t recd_app; uint8_t buff[max_to_send]; recd_app = stcp_app_recv(sd, buff, max_to_send); /* construct header */ uint32_t header_size = sizeof(struct tcphdr); uint8_t header_buf[header_size]; memset(header_buf, 0, header_size); struct tcphdr * tcp_header = ( struct tcphdr *) header_buf; tcp_header->th_seq = htonl(ctx->sent_last_byte_sent + 1); tcp_header->th_flags |= TH_SYN; tcp_header->th_off = 5; /* no options, data begins 20 bytes into packet */ /* set adv window size */ tcp_header->th_win = htons(calc_adv_wind(ctx)); ctx->sent_adv_window = calc_adv_wind(ctx); /* copy the data into the tcp buffer */ uint32_t wind_index = ((ctx->sent_last_byte_written + 1) - ctx->initial_sequence_num) % MAX_WINDOW_SIZE; our_dprintf("window index %u\n", wind_index); /* we're wrapping the buffer */ if (wind_index + recd_app > MAX_WINDOW_SIZE) { size_t delta = MAX_WINDOW_SIZE - wind_index; our_dprintf("wrapping the buffer\n"); /* fill the end of the buffer */ memcpy(ctx->send_wind + wind_index, buff, delta); /* restart at the beginning of the buffer */ memcpy(ctx->send_wind, buff+delta, recd_app - delta); stcp_network_send(sd, header_buf, header_size, ctx->send_wind + wind_index, delta, ctx->send_wind, recd_app - delta, NULL); } else { /* don't need to wrap the buffer */ our_dprintf("not wrapping the buffer\n"); memcpy(ctx->send_wind + wind_index, buff, recd_app); stcp_network_send(sd, header_buf, header_size, (ctx->send_wind + wind_index), recd_app, NULL); } ctx->sent_last_byte_sent += recd_app; ctx->sent_last_byte_written += recd_app; return 0; }
/* control_loop() is the main STCP loop; it repeatedly waits for one of the * following to happen: * - incoming data from the peer * - new data from the application (via mywrite()) * - the socket to be closed (via myclose()) * - a timeout */ static void control_loop(mysocket_t sd, context_t *ctx) { assert(ctx); int tcplen; char payload[MAXLEN]; STCPHeader *in_header; void *packtosend; char *indicpt; char tempbuff[WINLEN]; stcp_event_type_t eventflag; while (!ctx->done) { unsigned int event; eventflag = ANY_EVENT; /* set timeout if there is any unack'd data; if not, just make it NULL */ struct timespec timestart; struct timespec *timeout; if (ctx->send_unack < ctx->send_next) { timestart = ((packet_t *)list_get_at(ctx->unackd_packets, 0))->start_time; timeout = ×tart; /* constructs the timeout with absolute time by adding the RTO */ timeout->tv_sec += ctx->rto.tv_sec; timeout->tv_nsec += ctx->rto.tv_nsec; /* ensures no nanosecond overflow */ if (timeout->tv_nsec >= 1000000000) { timeout->tv_sec += timeout->tv_nsec / 1000000000; timeout->tv_nsec = timeout->tv_nsec % 1000000000; } if (timeout->tv_sec <= time(NULL)) /*earliest unacked packet has timed out, unless it's currently sitting in buffer */ eventflag = NETWORK_DATA|TIMEOUT; } else { timeout = NULL; DEBUG("No timeout set\n"); } /* see stcp_api.h or stcp_api.c for details of this function */ /* XXX: you will need to change some of these arguments! */ event = stcp_wait_for_event(sd, eventflag, timeout); /* check whether it was the network, app, or a close request */ if (event & APP_DATA) { DEBUG("Application Data\n"); /* the application has requested that data be sent */ /* we should send as long as send_wind > 0 */ int open_window = ctx->send_wind - (ctx->send_next - ctx->send_unack); if(open_window > 0) { int send_size; /* only read in as much from app as we can send */ if(open_window > MAXLEN) send_size = MAXLEN; else send_size = open_window; void* buffer = (char *) calloc(1, send_size); tcplen = stcp_app_recv(sd, buffer, send_size); if( tcplen > 0 ) { DEBUG("Application data size: %d\n", tcplen); /*create and send packet*/ packtosend = (void *)make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, tcplen); memcpy(packtosend + sizeof(STCPHeader), buffer, tcplen); stcp_network_send(sd, packtosend, sizeof(STCPHeader) + tcplen, NULL); DEBUG("Packet of payload size %d, ack number %d, seq number %d sent to network\n", tcplen, ctx->recv_next, ctx->send_next); packet_t_create(ctx, packtosend, sizeof(STCPHeader) + tcplen); /* now a packet has been pushed onto the unacked queue */ /* update window length and send_next */ ctx->send_next += tcplen; } free(buffer); buffer = NULL; free(packtosend); packtosend = NULL; } else DEBUG("Could not get application data\n"); } if (event & NETWORK_DATA) { DEBUG("Network Data\n"); in_header = malloc(sizeof(STCPHeader)); /* network data transmission */ int data_size = recv_packet(sd, ctx, payload, MAXLEN, in_header); DEBUG("Received net data size is %d\n", data_size); /* send ACK as long as it's not past our window, and actually contained data */ if(data_size > 0 || data_size == -2) { /* -2 indicates it received old data, which we should not send to the application */ packtosend = (void *)make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, 0); stcp_network_send(sd, packtosend, sizeof(STCPHeader), NULL); free(packtosend); packtosend = NULL; } /* send payload to application, if it's valid */ if(data_size > 0) { DEBUG("Sent data of size %d to application\n", data_size); stcp_app_send(sd, ctx->recv_buffer, data_size); /* slide the window over */ indicpt = strchr(ctx->recv_indicator, '\0'); data_size = indicpt - ctx->recv_indicator; memcpy(tempbuff, ctx->recv_buffer + data_size, WINLEN - data_size); memset(ctx->recv_buffer, '\0', WINLEN); memcpy(ctx->recv_buffer, tempbuff, WINLEN - data_size); /* slide window indicator over */ memcpy(tempbuff, indicpt, WINLEN - data_size); memset(ctx->recv_indicator, '\0', WINLEN); memcpy(ctx->recv_indicator, tempbuff, WINLEN - data_size); } /* deal with connection teardown, if need be */ if (in_header->th_flags & TH_ACK) { /* go from FIN-WAIT1 --> FIN-WAIT2 */ if(ctx->connection_state == CSTATE_FIN_WAIT1) { DEBUG("State: FIN-WAIT2\n"); ctx->connection_state = CSTATE_FIN_WAIT2; } /* go from LAST-ACK --> CLOSED */ if(ctx->connection_state == CSTATE_LAST_ACK) { DEBUG("State: CLOSED\n"); ctx->connection_state = CSTATE_CLOSED; free(in_header); in_header = NULL; break; } /* go from CLOSING --> CLOSED */ if(ctx->connection_state == CSTATE_CLOSING) { DEBUG("State: CLOSED\n"); ctx->connection_state = CSTATE_CLOSED; free(in_header); in_header = NULL; break; } } /* branching for the receipt of a FIN packet */ if (in_header->th_flags & TH_FIN) { DEBUG("Received FIN packet\n"); /* Acknowledge FIN, which counts as a byte */ ctx->recv_next++; packtosend = (void *)make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, 0); stcp_network_send(sd, packtosend, sizeof(STCPHeader), NULL); /* go into CLOSE-WAIT */ if (ctx->connection_state == CSTATE_ESTABLISHED) { DEBUG("State: CLOSE_WAIT\n"); /* inform app of FIN */ stcp_fin_received(sd); ctx->connection_state = CSTATE_CLOSE_WAIT; } /* go from FIN-WAIT2 --> CLOSED */ if (ctx->connection_state == CSTATE_FIN_WAIT2) { DEBUG("State: CLOSED\n"); ctx->connection_state = CSTATE_CLOSED; free(in_header); in_header = NULL; break; } /* go from FIN-WAIT1 --> CLOSING */ if (ctx->connection_state == CSTATE_FIN_WAIT1) { DEBUG("State: CLOSING\n"); /* inform app of FIN */ stcp_fin_received(sd); ctx->connection_state = CSTATE_CLOSING; } free(in_header); in_header = NULL; } } if (event & APP_CLOSE_REQUESTED) { DEBUG("Application close requested\n"); if(ctx->connection_state == CSTATE_ESTABLISHED) { /* need to send all outstanding data first */ DEBUG("Sending FIN packet with seq %d, ack %d\n", ctx->send_next, ctx->recv_next); STCPHeader *header = make_stcp_packet(TH_FIN, ctx->send_next, ctx->recv_next, 0); stcp_network_send(sd, header, sizeof(STCPHeader), NULL); ctx->send_unack += 1; packet_t_create(ctx, header, sizeof(STCPHeader)); free(header); header = NULL; /* go into FIN-WAIT1 */ ctx->connection_state = CSTATE_FIN_WAIT1; DEBUG("State: FIN-WAIT1\n"); } if(ctx->connection_state == CSTATE_CLOSE_WAIT) { DEBUG("Sending FIN packet with seq %d, ack %d\n", ctx->send_next, ctx->recv_next); STCPHeader *header = make_stcp_packet(TH_FIN, ctx->send_next, ctx->recv_next, 0); stcp_network_send(sd, header, sizeof(STCPHeader), NULL); packet_t_create(ctx, header, sizeof(STCPHeader)); ctx->send_next += 1; free(header); header = NULL; /* go from CLOSE-WAIT --> LAST-ACK */ ctx->connection_state = CSTATE_LAST_ACK; DEBUG("State: LAST-ACK\n"); } } if (event == TIMEOUT) { int giveup = 0; /* TIMEOUT--resend all packets in ctx->unackd_packets */ packet_t *resendpack; list_iterator_start(ctx->unackd_packets); while (list_iterator_hasnext(ctx->unackd_packets)) { resendpack = (packet_t *)list_iterator_next(ctx->unackd_packets); if (resendpack->retry_count > 6) { /*close the connection*/ DEBUG("Too many retries"); errno = ECONNABORTED; giveup = 1; } /* increment retries */ resendpack->retry_count++; clock_gettime(CLOCK_REALTIME, &(resendpack->start_time)); ((STCPHeader *)(resendpack->packet))->th_ack = htonl(ctx->recv_next); stcp_network_send(sd, resendpack->packet, resendpack->packet_size, NULL); DEBUG("Resent packet with sequence number %d\n", ntohl(((STCPHeader *)(resendpack->packet))->th_seq)); } list_iterator_stop(ctx->unackd_packets); if (giveup) break; } } }