/* * Send a message to the other end of the socket. * * The send call should block until the remote host has ACKnowledged receipt of * the message. This does not necessarily imply that the application has called * 'minisocket_receive', only that the packet is buffered pending a future * receive. * * It is expected that the order of calls to 'minisocket_send' implies the order * in which the concatenated messages will be received. * * 'minisocket_send' should block until the whole message is reliably * transmitted or an error/timeout occurs * * Arguments: the socket on which the communication is made (socket), the * message to be transmitted (msg) and its length (len). * Return value: returns the number of successfully transmitted bytes. Sets the * error code and returns -1 if an error is encountered. */ int minisocket_send(minisocket_t socket, minimsg_t msg, int len, minisocket_error *error) { int portNumber; int sentLength; int sentData = 0; int maxDataSize; int check; //interrupt_level_t prev_level; if (error == NULL) return -1; if (socket == NULL || msg == NULL || len <= 0) { *error = SOCKET_INVALIDPARAMS; return -1; } portNumber = socket->port_number; if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_TO_CLOSE || minisockets[portNumber] == NULL) { *error = SOCKET_SENDERROR; return -1; } /*prev_level = set_interrupt_level(DISABLED); socket->num_waiting_on_mutex++; semaphore_P(socket->mutex); socket->num_waiting_on_mutex--; set_interrupt_level(prev_level); */ if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_TO_CLOSE || minisockets[portNumber] == NULL) { *error = SOCKET_SENDERROR; // semaphore_V(socket->mutex); return -1; } maxDataSize = MAX_NETWORK_PKT_SIZE - sizeof(struct mini_header_reliable); while (len > 0) { sentLength = (maxDataSize > len ? len : maxDataSize); check = transmit_packet(socket, socket->destination_addr, socket->destination_port, 1, MSG_ACK, sentLength, (msg+sentData), error); if (check == -1) { // semaphore_V(socket->mutex); minisocket_destroy(socket, 0); return (sentData == 0 ? -1 : sentData); } len -= maxDataSize; sentData += sentLength; } // semaphore_V(socket->mutex); *error = SOCKET_NOERROR; return sentData; }
/* Close a connection. If minisocket_close is issued, any send or receive should * fail. As soon as the other side knows about the close, it should fail any * send or receive in progress. The minisocket is destroyed by minisocket_close * function. The function should never fail. */ void minisocket_close(minisocket_t socket) { interrupt_level_t l; minisocket_error error; resend_arg_t resend_alarm_arg; resend_alarm_arg = (resend_arg_t)calloc(1,sizeof(struct resend_arg)); if (!resend_alarm_arg){ return; } error = SOCKET_NOERROR; //printf("in minisocket_close\n"); //set state to close_wait //send close, wait using alarms //(after 7 retries, close connection) //P on ack_ready //if ack_ready, close connection //ill formed argument if (!sock_array){ free(resend_alarm_arg); return; } l = set_interrupt_level(DISABLED); //ill formed argument if (sock_array[socket->src_port] == NULL){ set_interrupt_level(l); free(resend_alarm_arg); return; } if (socket->curr_state == CLOSE_SEND || socket->curr_state == CLOSE_RCV){ set_interrupt_level(l); free(resend_alarm_arg); return; } socket->curr_state = CLOSE_SEND; socket->try_count = 0; socket->curr_seq++; minisocket_send_ctrl( MSG_FIN, socket, &error); //printf("in minisocket_close, sent my MSG_FIN\n"); resend_alarm_arg->sock = socket; resend_alarm_arg->msg_type = MSG_FIN; resend_alarm_arg->data_len = 0; resend_alarm_arg->data = (char*) &socket; //placeholder resend_alarm_arg->error = &error; if (socket->resend_alarm){ deregister_alarm(socket->resend_alarm); } socket->resend_alarm = NULL; socket->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, resend_alarm_arg, minithread_time()); //printf("in minisocket_close, set my alarm.\n"); set_interrupt_level(l); semaphore_P(socket->ack_ready_sem); //wait for ack packet... //received ack, close connection //printf("in minisocket_close, got my ACK baby\n"); l = set_interrupt_level(DISABLED); if (socket->resend_alarm){ deregister_alarm(socket->resend_alarm); } socket->resend_alarm = NULL; minisocket_destroy(socket, &error); //printf("Closed. Error is %d", error); if (error != SOCKET_NOERROR){ //printf("Something went wrong. Close connection failure.\n"); } set_interrupt_level(l); //printf("in minisocket_close, SUCCESS\n"); free(resend_alarm_arg); return; }
void delete_socket(void* arg) { minisocket_t socket = (minisocket_t) arg; minisocket_destroy(socket, 0); }
void self_destruct(void* arg) { minisocket_error error; minisocket_destroy((minisocket_t)arg, &error); //printf("Goodby cruel world\n"); return; }
/* Initiate the communication with a remote site. When communication is * established create a minisocket through which the communication can be made * from now on. * * The first argument is the network address of the remote machine. * * The argument "port" is the port number on the remote machine to which the * connection is made. The port number of the local machine is one of the free * port numbers. * * Return value: the minisocket_t created, otherwise NULL with the errorcode * stored in the "error" variable. */ minisocket_t minisocket_client_create(network_address_t addr, int port, minisocket_error *error) { minisocket_t new_sock; interrupt_level_t l; resend_arg* resend_alarm_arg; unsigned short start; char tmp; //check valid portnum if (port < 0 || port >= CLIENT_START) { *error = SOCKET_INVALIDPARAMS; return NULL; } semaphore_P(client_lock); start = curr_client_idx; while (sock_array[curr_client_idx] != NULL){ curr_client_idx++; if (curr_client_idx >= NUM_SOCKETS){ curr_client_idx = CLIENT_START; } if (curr_client_idx == start){ // client sockets full semaphore_V(client_lock); *error = SOCKET_NOMOREPORTS; return NULL; } } new_sock = (minisocket_t)malloc(sizeof(struct minisocket)); if (!new_sock){ semaphore_V(client_lock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->pkt_ready_sem = semaphore_create(); if (!(new_sock->pkt_ready_sem)){ semaphore_V(client_lock); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->pkt_q = queue_new(); if (!(new_sock->pkt_q)){ semaphore_V(client_lock); semaphore_destroy(new_sock->pkt_ready_sem); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->sock_lock = semaphore_create(); if (!(new_sock->sock_lock)){ semaphore_V(client_lock); semaphore_destroy(new_sock->pkt_ready_sem); queue_free(new_sock->pkt_q); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->ack_ready_sem = semaphore_create(); if (!(new_sock->ack_ready_sem)){ semaphore_V(client_lock); semaphore_destroy(new_sock->pkt_ready_sem); queue_free(new_sock->pkt_q); semaphore_destroy(new_sock->sock_lock); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } semaphore_initialize(new_sock->pkt_ready_sem, 0); semaphore_initialize(new_sock->ack_ready_sem, 0); semaphore_initialize(new_sock->sock_lock, 1); new_sock->curr_state = CONNECT_WAIT; new_sock->try_count = 0; new_sock->curr_ack = 0; new_sock->curr_seq = 1; new_sock->resend_alarm = NULL; //no alarm set new_sock->src_port = curr_client_idx; new_sock->dst_port = port; network_address_copy(addr, new_sock->dst_addr); sock_array[curr_client_idx] = new_sock; semaphore_V(client_lock); l = set_interrupt_level(DISABLED); minisocket_send_ctrl(MSG_SYN, new_sock, error); resend_alarm_arg = (resend_arg*)calloc(1, sizeof(resend_arg)); resend_alarm_arg->sock = new_sock; resend_alarm_arg->msg_type = MSG_SYN; resend_alarm_arg->data_len = 0; resend_alarm_arg->data = &tmp; //placeholder resend_alarm_arg->error = error; new_sock->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, resend_alarm_arg, minithread_time()); set_interrupt_level(l); semaphore_P(new_sock->ack_ready_sem); l = set_interrupt_level(DISABLED); switch(new_sock->curr_state) { case CONNECTED: //we are connected // must have gotten a MSG_SYNACK new_sock->curr_state = CONNECTED; new_sock->try_count = 0; if (new_sock->resend_alarm){ deregister_alarm(new_sock->resend_alarm); } new_sock->resend_alarm = NULL; *error = SOCKET_NOERROR; set_interrupt_level(l); break; case CLOSE_RCV: *error = SOCKET_BUSY; minisocket_destroy(new_sock, error); if (new_sock->resend_alarm){ deregister_alarm(new_sock->resend_alarm); } new_sock->resend_alarm = NULL; *error = SOCKET_BUSY; set_interrupt_level(l); new_sock = NULL; break; default: // error *error = SOCKET_NOSERVER; minisocket_destroy(new_sock, error); set_interrupt_level(l); new_sock = NULL; break; } free(resend_alarm_arg); return new_sock; }
void minisocket_close(minisocket_t *socket) { if (socket == NULL || mini_socket_data[socket->local_port_number] == NULL || socket->state == CLOSED) { return; // no cleanup necessary } fprintf(stderr, "Closing time \n"); mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, socket->remote_address, MSG_FIN); int send_success = 0; int timeout = START_TIMEOUT / 2; for (int i = 0; i < MAX_FAILURES; i++) { if (socket->state == CLOSED) { return; //received a MSG_FIN message in network_handler, so stop re-tries } timeout = timeout * 2; network_send_pkt(socket->remote_address, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; //keep looking through received packets until either the alarm fires or it finds the correct packet while (!send_success && !alarm_fired) { semaphore_P(socket->ack_ready); //alarm has fired, since there are no packets to be received if (queue_length(socket->acknowledgements) == 0) { printf("CLOSE queue length: %d\n", queue_length(socket->acknowledgements)); alarm_fired = 1; //goes to next iteration of for loop } //There is a packet (alarm hasnt fired yet) else { network_interrupt_arg_t* interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); // verify non null message if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; //already checked port and address in network_handler if (received_header->message_type == MSG_ACK) { deregister_alarm(timeout_alarm_id); //only deregister alarm when the right packet is found send_success = 1; free(interrupt_message); break; } else { free(interrupt_message); } } } } if (send_success) { break; } } printf("out of CLOSE\n"); //sender considers the connection closed, even if MSG_ACK not received if (socket->state != CLOSED) { printf("calling DESTROY from CLOSE\n"); minisocket_destroy(socket); } }
int minisocket_send(minisocket_t *socket, const char *msg, int len, minisocket_error *error) { //Check params if (socket == NULL || mini_socket_data[socket->local_port_number] != socket || len < 0 || msg == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (socket->state == CLOSED) { *error = SOCKET_SENDERROR; return -1; } if (len == 0) { return 0; } //can't send more bytes than we have if (len > strlen(msg)) { len = strlen(msg); } int bytes_sent = 0; int max_data_size = MAX_NETWORK_PKT_SIZE - sizeof(mini_header_reliable_t); int data_left = len; int packet_size; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, socket->remote_address, MSG_ACK); //Keep sending until all is sent while (data_left != 0) { if (data_left >= max_data_size) { packet_size = max_data_size; } else { packet_size = data_left; } data_left -= packet_size; pack_unsigned_int(header.seq_number, socket->seq); int send_success = 0; //Attempt to send message int timeout = START_TIMEOUT/2; for (int i = 0; i < MAX_FAILURES; i++) { //printf("i: %d\n", i); timeout = timeout * 2; //update the seq and ack numbers for the header from the socket //printf("seq: %u, ack: %u\n", socket->seq, socket->ack); pack_unsigned_int(header.ack_number, socket->ack); pack_unsigned_int(header.seq_number, socket->seq); network_send_pkt(socket->remote_address, sizeof(mini_header_reliable_t), (char *) &header, packet_size, (char *) msg); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; //keep looking through received packets until either the alarm fires or it finds the correct packet while (!send_success && !alarm_fired) { semaphore_P(socket->ack_ready); if (queue_length(socket->acknowledgements) == 0) { // printf("no message in queue\n"); alarm_fired = 1; //goes to next iteration of for loop } else { // printf("length of queue currently %d\n", queue_length(socket->acknowledgements)); network_interrupt_arg_t* interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); unsigned int new_ack = unpack_unsigned_int(received_header->ack_number); //printf("Received an acknowledgment with ack number %u \n", new_ack); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0 && received_header->message_type == MSG_ACK && new_ack == (socket->seq + packet_size)) { //same address, same ports, right message, right ack deregister_alarm(timeout_alarm_id); //only deregister alarm when the right packet is found send_success = 1; bytes_sent += packet_size; msg += packet_size; socket->seq += packet_size; free(interrupt_message); break; } else { free(interrupt_message); } } } } if (send_success) { break; } } if (!send_success) { //Got a timeout, should close down socket *error = SOCKET_SENDERROR; minisocket_destroy(socket); return bytes_sent; } } //printf("\n\n"); return bytes_sent; }
minisocket_t* minisocket_client_create(const network_address_t addr, int port, minisocket_error *error) { //Check socket number first if (valid_server_port(port) == 0) { *error = SOCKET_INVALIDPARAMS; return NULL; } //Then check if we can make another client if (client_count >= MAX_CLIENTS) { *error = SOCKET_NOMOREPORTS; return NULL; } //create new minisocket minisocket_t* socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (socket == NULL) { *error = SOCKET_OUTOFMEMORY; return NULL; //OOM } while (mini_socket_data[next_port]) { next_port = ((next_port + 1) % PORT_RANGE); } socket->socket_type = CLIENT_TYPE; socket->local_port_number = next_port; next_port = ((next_port + 1) % PORT_RANGE); socket->state = CONNECTING; socket->seq = 0; socket->ack = 0; network_address_copy(addr, socket->remote_address); socket->remote_port_number = (unsigned short) port; network_address_copy(addr, socket->remote_address); socket->next_read = 0; network_address_t my_address; network_get_my_address(my_address); network_address_copy(my_address, socket->local_address); socket->datagrams_ready = semaphore_create(); semaphore_initialize(socket->datagrams_ready, 0); socket->ack_ready = semaphore_create(); semaphore_initialize(socket->ack_ready, 0); socket->incoming_data = queue_new(); socket->acknowledgements = queue_new(); //add socket to global array mini_socket_data[socket->local_port_number] = socket; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, addr, MSG_SYN); //minisocket and header now created, try to establish connection pack_unsigned_int(header.seq_number, socket->seq); pack_unsigned_int(header.ack_number, socket->ack); //send first message and wait for response int response = 0; //no response yet int timeout = START_TIMEOUT / 2; //this way first timeout will be at START_TIMEOUT for (int i = 0; i < MAX_FAILURES; i++) { printf("sending MSG_SYN in client, i: %d\n", i); timeout = timeout * 2; network_send_pkt(addr, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; while (!response && !alarm_fired) { semaphore_P(socket->ack_ready); //If queue length == 0, then the alarm must have fired if (queue_length(socket->acknowledgements) == 0) { alarm_fired = 1; //goes to next iteration of for loop } //otherwise, we received a packet else { network_interrupt_arg_t *interrupt_message; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); // if message not null if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0) { //if SYNACK printf("CLIENT\n"); if (received_header->message_type == MSG_SYNACK) { printf("GOT message SYN_ACK\n"); deregister_alarm(timeout_alarm_id); response = 1; break; } //if MSG_FIN else if (received_header->message_type == MSG_FIN) { printf("got message MSG_FIN in client\n"); //server already in use deregister_alarm(timeout_alarm_id); *error = SOCKET_BUSY; response = 1; minisocket_destroy(socket); return NULL; } //WRONG MESSAGE TYPE else { printf("wrong message type received in client\n"); //TODO : try another message type, maybe do nothing? } } } } } if (response) { break; } } // timeout after 12.8s occured, close down socket if (response != 1) { printf("full timeout in client sending SYN\n"); *error = SOCKET_NOSERVER; minisocket_destroy(socket); return NULL; } // send final MSG_ACK once to server (future data packets will also have MSG_ACK as header type) header.message_type = MSG_ACK; printf("sending final MSG_ACK and leaving client create\n"); network_send_pkt(addr, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); socket->state = CONNECTED; client_count++; return socket; }
/* Close a connection. If minisocket_close is issued, any send or receive should * fail. As soon as the other side knows about the close, it should fail any * send or receive in progress. The minisocket is destroyed by minisocket_close * function. The function should never fail. */ void minisocket_close(minisocket_t socket) { minisocket_destroy(socket, 1); }
/* * Send a message to the other end of the socket. * * The send call should block until the remote host has ACKnowledged receipt of * the message. This does not necessarily imply that the application has called * 'minisocket_receive', only that the packet is buffered pending a future * receive. * * It is expected that the order of calls to 'minisocket_send' implies the order * in which the concatenated messages will be received. * * 'minisocket_send' should block until the whole message is reliably * transmitted or an error/timeout occurs * * Arguments: the socket on which the communication is made (socket), the * message to be transmitted (msg) and its length (len). * Return value: returns the number of successfully transmitted bytes. Sets the * error code and returns -1 if an error is encountered. */ int minisocket_send(minisocket_t socket, minimsg_t msg, int len, minisocket_error *error) { int ret; int max_data_size; int data_sent = 0; int len_sent; int port_number; if (socket == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (msg == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (len <= 0) { *error = SOCKET_INVALIDPARAMS; return -1; } if (error == NULL) { return -1; } port_number = socket->port_number; // First, check if the socket is closing if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE || minisockets[port_number] == NULL) { *error = SOCKET_SENDERROR; return -1; } socket->num_waiting_on_mutex++; semaphore_P(socket->mutex); socket->num_waiting_on_mutex--; // Check again - maybe we were awoken because it's now closing if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE || minisockets[port_number] == NULL) { *error = SOCKET_SENDERROR; semaphore_V(socket->mutex); return -1; } // Determine the max data size max_data_size = MAX_NETWORK_PKT_SIZE - sizeof(struct mini_header_reliable); if (len > max_data_size) { // Partition the large message into smaller packets while (len > 0) { // See how much we can fit into the next packet len_sent = (max_data_size > len ? len : max_data_size); // Send the packet ret = transmit_packet(socket, socket->dst_addr, socket->dst_port, 1, MSG_ACK, len_sent, msg+data_sent, error); // Check for errors if (ret == -1) { // If we can't reach the host, close the connection semaphore_V(socket->mutex); minisocket_destroy(socket, 0); return (data_sent == 0 ? -1 : data_sent); } // Update the amount of data left to send len -= max_data_size; // can also be - len_sent, doesn't matter // Update the number of bytes we've sent - also used w/ msg as a ptr data_sent += len_sent; } } else { // This can be transmitted as a single packet ret = transmit_packet(socket, socket->dst_addr, socket->dst_port, 1, MSG_ACK, len, msg, error); if (ret == -1) { // If we can't reach the host, close the connection semaphore_V(socket->mutex); minisocket_destroy(socket, 0); return -1; } data_sent = len; } semaphore_V(socket->mutex); *error = SOCKET_NOERROR; return data_sent; }