/* *************************************************** * Function: transport_init * *************************************************** * initialise the transport layer, and start the main loop, handling * any data from the peer or the application. this function should not * return until the connection is closed. */ void transport_init(mysocket_t sd, bool_t is_active) { context_t *ctx = init_ctx(is_active); bool success = false; if (is_active) { success = active_init(sd, ctx); } else { success = passive_init(sd, ctx); } if (success){ ctx->connection_state = CSTATE_ESTABLISHED; stcp_unblock_application(sd); control_loop(sd, ctx); } else { errno = ETIMEDOUT; stcp_unblock_application(sd); } teardown_resources(ctx); /* Wait for potential pending processes in * other side (this is to fix a bug in the starter code) */ sleep(1); our_dprintf("Exiting Loop!\n"); }
/* initialise the transport layer, and start the main loop, handling * any data from the peer or the application. this function should not * return until the connection is closed. */ void transport_init(mysocket_t sd, bool_t is_active) { context_t *ctx; ctx = (context_t *) calloc(1, sizeof(context_t)); assert(ctx); generate_initial_seq_num(ctx); ctx->connection_state = CSTATE_CLOSED; //init all buffer /* XXX: you should send a SYN packet here if is_active, or wait for one * to arrive if !is_active. after the handshake completes, unblock the * application with stcp_unblock_application(sd). you may also use * this to communicate an error condition back to the application, e.g. * if connection fails; to do so, just set errno appropriately (e.g. to * ECONNREFUSED, etc.) before calling the function. */ if (!transport_3way_handshake(sd, ctx)) { stcp_unblock_application(sd); free(ctx); return; } //init send and recv buffer here ctx->connection_state = CSTATE_ESTABLISHED; stcp_unblock_application(sd); control_loop(sd, ctx); /* do any cleanup here */ free(ctx); }
/* transport layer thread; transport_init() should not return until the * transport layer finishes (i.e. the connection is over). */ static void *transport_thread_func(void *arg_ptr) { mysock_context_t *ctx = (mysock_context_t *) arg_ptr; char eof_packet; assert(ctx); ASSERT_VALID_MYSOCKET_DESCRIPTOR(ctx, ctx->my_sd); /* enter the STCP control loop. transport_init() doesn't return until the * connection's finished. that function should first signal establishment * of the connection after SYN/SYN-ACK (or an error condition if the * connection couldn't be established) to the application by using * stcp_unblock_application(); as the name suggests, this unblocks the * calling code. transport_init() then handles the connection, * returning only after the connection is closed. */ transport_init(ctx->my_sd, ctx->is_active); /* transport_init() has returned; both sides have closed the connection, * do some final cleanup here... */ PTHREAD_CALL(pthread_mutex_lock(&ctx->blocking_lock)); if (ctx->blocking) { /* if we're still blocked, STCP must not have indicated the * connection completed. pass the error up to the application. */ if (errno == 0 || errno == EINTR) { /* this is a bit of a kludge--this should really be set by STCP * itself, but it's a reasonable guess if for some reason (e.g. * oversight) the transport layer hasn't announced why it * bailed out... */ errno = (ctx->is_active) ? ECONNREFUSED : ECONNABORTED; } PTHREAD_CALL(pthread_mutex_unlock(&ctx->blocking_lock)); stcp_unblock_application(ctx->my_sd); } else { PTHREAD_CALL(pthread_mutex_unlock(&ctx->blocking_lock)); } /* force final myread() to return 0 bytes (this should have been done * by the transport layer already in response to the peer's FIN). */ _mysock_enqueue_buffer(ctx, &ctx->app_send_queue, &eof_packet, 0); return NULL; }
/* initialise the transport layer, and start the main loop, handling * any data from the peer or the application. this function should not * return until the connection is closed. */ void transport_init(mysocket_t sd, bool_t is_active) { context_t *ctx; int res; ctx = (context_t *) calloc(1, sizeof(context_t)); assert(ctx); generate_initial_seq_num(ctx); ctx->connection_state = CSTATE_CLOSED; ctx->send_wind = (uint8_t *) malloc(MAX_WINDOW_SIZE); memset(ctx->send_wind, 0, MAX_WINDOW_SIZE); ctx->recv_wind = (uint8_t *) malloc(MAX_WINDOW_SIZE); memset(ctx->recv_wind, 0, MAX_WINDOW_SIZE); res = open_tcp_conn(sd, ctx, is_active); /* XXX: you should send a SYN packet here if is_active, or wait for one * to arrive if !is_active. after the handshake completes, unblock the * application with stcp_unblock_application(sd). you may also use * this to communicate an error condition back to the application, e.g. * if connection fails; to do so, just set errno appropriately (e.g. to * ECONNREFUSED, etc.) before calling the function. */ stcp_unblock_application(sd); if (ctx->connection_state == CSTATE_ESTABLISHED){ control_loop(sd, ctx); } else if (ctx->connection_state == CSTATE_FIN_WAIT_1){ close_tcp_conn(sd, ctx); } else if (ctx->connection_state == CSTATE_CLOSED) { ; }else { our_dprintf("bad state in transport init"); assert(0); } /* do any cleanup here */ free(ctx->send_wind); free(ctx->recv_wind); free(ctx); }
/* initialise the transport layer, and start the main loop, handling * any data from the peer or the application. this function should not * return until the connection is closed. */ void transport_init(mysocket_t sd, bool_t is_active) { context_t *ctx; uint8_t flags; int tcplen; ctx = (context_t *) calloc(1, sizeof(context_t)); assert(ctx); /* ensures this array begins as entirely null characters */ memset(ctx->recv_indicator, '\0', WINLEN); char buffer[sizeof(STCPHeader) + MAXLEN]; generate_initial_seq_num(ctx); ctx->connection_state = CSTATE_CLOSED; list_t unackd; ctx->unackd_packets = &unackd; list_init(ctx->unackd_packets); /* used for setting timeouts */ struct timespec timestart; struct timespec *timeout; /* XXX: you should send a SYN packet here if is_active, or wait for one * to arrive if !is_active. after the handshake completes, unblock the * application with stcp_unblock_application(sd). you may also use * this to communicate an error condition back to the application, e.g. * if connection fails; to do so, just set errno appropriately (e.g. to * ECONNREFUSED, etc.) before calling the function. */ if(is_active) { ctx->send_unack = ctx->initial_sequence_num; STCPHeader *header = make_stcp_packet(TH_SYN, ctx->send_unack, 0, 0); /* first step in the handshake: sending a SYN packet */ tcplen = stcp_network_send(sd, header, sizeof(STCPHeader), NULL); ctx->send_next = ctx->send_unack; /* this ensures the SYN packet will be resent if it is dropped/times out */ packet_t_create(ctx, header, sizeof(STCPHeader)); DEBUG("Sent SYN packet with seq number %d\n", ntohl(header->th_seq)); if(tcplen < 0) { DEBUG("SYN send failed"); errno = ECONNREFUSED; } else { ctx->send_next++; ctx->connection_state = CSTATE_SYN_SENT; /* the client now waits for a SYN/ACK */ while(ctx->connection_state == CSTATE_SYN_SENT) { timestart = ((packet_t *)list_get_at(ctx->unackd_packets, 0))->start_time; timeout = ×tart; timeout->tv_sec += ctx->rto.tv_sec; timeout->tv_nsec += ctx->rto.tv_nsec; if (timeout->tv_nsec >= 1000000000) { timeout->tv_sec += timeout->tv_nsec / 1000000000; timeout->tv_nsec = timeout->tv_nsec % 1000000000; } unsigned int event = stcp_wait_for_event(sd, NETWORK_DATA, timeout); /* yes we need to add timeout later */ if(event & NETWORK_DATA) { /* we now expect the next packet to be a SYN-ACK */ tcplen = recv_packet(sd, ctx, NULL, 0, header); flags = (TH_SYN|TH_ACK); if( tcplen < 0 || !((header->th_flags & TH_SYN)&&(header->th_flags & TH_ACK))) { DEBUG("Did not receive SYN/ACK\n"); continue; } else { DEBUG("packet received with seq %d and ack %d\n", ntohl(header->th_seq), ntohl(header->th_ack)); if(ntohl(header->th_ack) == ctx->send_next) { header = make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, 0); tcplen = stcp_network_send(sd, header, sizeof(STCPHeader), NULL); /* the client now sends an ACK packet, and goes to the ESTABLISHED state */ DEBUG("Sent ACK packet with seq number %d\n", ntohl(header->th_seq)); if(tcplen < 0) { DEBUG("ACK send failed"); errno = ECONNABORTED; break; } ctx->connection_state = CSTATE_ESTABLISHED; free(header); header = NULL; } } } } } } if(!is_active) { /* wait for a SYN packet */ while(ctx->connection_state == CSTATE_CLOSED) { ctx->send_unack = ctx->initial_sequence_num; ctx->send_next = ctx->send_unack; unsigned int event = stcp_wait_for_event(sd, NETWORK_DATA|TIMEOUT, NULL); if(event & NETWORK_DATA) { STCPHeader *header = (STCPHeader *) malloc(sizeof(STCPHeader)); tcplen = recv_packet(sd, ctx, NULL, 0, header); if(tcplen < 0 || header->th_flags != TH_SYN) { DEBUG("Did not receive SYN packet\n"); continue; } else { DEBUG("Received SYN with seq number %d\n", ntohl(header->th_seq)); ctx->connection_state = CSTATE_SYN_RECEIVED; header = make_stcp_packet(TH_SYN|TH_ACK, ctx->send_unack, ctx->recv_next, 0); tcplen = stcp_network_send(sd, header, sizeof(STCPHeader), NULL); packet_t_create(ctx, header, sizeof(STCPHeader)); /* send out a SYN/ACK packet to the initiating client */ DEBUG("SYN/ACK sent with ack number %d and seq number %d\n", htonl(header->th_ack), htonl(header->th_seq)); if(tcplen < 0) { DEBUG("SYN/ACK send failed"); errno = ECONNABORTED; break; } /* ensures the SYN-ACK will be resent if it is not ACK'd */ packet_t_create(ctx, header, sizeof(STCPHeader)); ctx->send_next++; } } } while(ctx->connection_state == CSTATE_SYN_RECEIVED) { timestart = ((packet_t *)list_get_at(ctx->unackd_packets, 0))->start_time; timeout = ×tart; timeout->tv_sec += ctx->rto.tv_sec; timeout->tv_nsec += ctx->rto.tv_nsec; if (timeout->tv_nsec >= 1000000000) { timeout->tv_sec += timeout->tv_nsec / 1000000000; timeout->tv_nsec = timeout->tv_nsec % 1000000000; } unsigned int event = stcp_wait_for_event(sd, NETWORK_DATA, timeout); if(event & NETWORK_DATA) { STCPHeader *header = (STCPHeader *) buffer; tcplen = recv_packet(sd, ctx, NULL, 0, header); if(tcplen < 0 || !(header->th_flags & TH_ACK)) { DEBUG("Did not receive ACK packet\n"); continue; } /* if it's the right ACK packet, go to the ESTABLISHED state */ if(ntohl(header->th_ack) == ctx->send_next) { DEBUG("Received ACK with ack number %d\n", ntohl(header->th_ack)); ctx->connection_state = CSTATE_ESTABLISHED; } } } } if(ctx->connection_state == CSTATE_ESTABLISHED) DEBUG("State: seq %d ack %d send window %d\n", ctx->send_next, ctx->recv_next, ctx->send_wind); /* unblocks the application */ /* relays possible error conditions */ stcp_unblock_application(sd); control_loop(sd, ctx); /* do any cleanup here */ free(ctx); }