int arq_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { int n; struct arq_packet ack_buf; struct sockaddr cliaddr; socklen_t cliaddr_len; struct arq_packet rbuf; size_t recv_len, data_len; unsigned char data_recvd; recv_len = 0; data_len = 0; data_recvd = 0; while (!data_recvd) { if (src_addr != NULL) { memcpy(&cliaddr, src_addr, *addrlen); cliaddr_len = *addrlen; } else { cliaddr_len = sizeof(cliaddr); } recv_len = HEADER_SZ + MIN(len, DATA_SZ); n = recvfrom(sockfd, &rbuf, recv_len, flags, &cliaddr, &cliaddr_len); if (-1 == n) { return -1; } /* assign initial recv_seq number */ if (-1 == recv_seq) { recv_seq = rbuf.seq ? 0 : 1; } if (n >= HEADER_SZ && rbuf.type == TYPE_DATA && rbuf.seq != recv_seq) { /* received a valid packet */ /* save the data */ data_len = n - HEADER_SZ; memcpy(buf, rbuf.data, data_len); /* next sequence number */ recv_seq = recv_seq ? 0 : 1; data_recvd = 1; } /* send an ACK */ ack_buf.seq = rbuf.seq; ack_buf.type = TYPE_ACK; n = unreliable_sendto(sockfd, &ack_buf, ACK_SZ, 0, &cliaddr, cliaddr_len); if (-1 == n) { return -1; } } /* update the receive addresses */ if (src_addr != NULL) { memcpy(src_addr, &cliaddr, sizeof(cliaddr)); *addrlen = cliaddr_len; } return data_len; }
int arq_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { int n; struct timeval tv; fd_set rd_set; struct arq_packet ack_buf; struct arq_packet sbuf; size_t send_len, data_len; unsigned char ack_recvd; unsigned char num_resend; /* build the ARQ packet to be sent */ send_len = HEADER_SZ + MIN(len, DATA_SZ); memcpy(&sbuf.data, buf, MIN(len, DATA_SZ)); sbuf.seq = seq; sbuf.type = TYPE_DATA; /* 'send_len' might be less than the requested 'len' */ /* main loop to send the packet and wait for an ACK */ ack_recvd = 0; num_resend = 0; while (!ack_recvd) { /* send the ARQ packet */ n = unreliable_sendto(sockfd, &sbuf, send_len, flags, dest_addr, addrlen); if (-1 == n) { return -1; } /* 'n' might be less than the requested 'send_len' */ data_len = n - HEADER_SZ; /* Actual amount of data sent minus headers, * used for the return value on success. */ /* wait for data or a time out */ FD_ZERO(&rd_set); FD_SET(sockfd, &rd_set); tv.tv_sec = 0; tv.tv_usec = TIMEOUT_MS*1000; n = select(sockfd+1, &rd_set, NULL, NULL, &tv); if (-1 == n) { return -1; } else if (0 == n) { /* timeout */ if (++num_resend > MAX_RESEND) return 0; /* give up */ continue; /* retry */ } n = recvfrom(sockfd, (void *) &ack_buf, ACK_SZ, 0, NULL, NULL); if (-1 == n) { return -1; } if (n >= HEADER_SZ && ack_buf.type == TYPE_ACK && ack_buf.seq == seq) { /* ACK confirmed */ seq = (seq) ? 0 : 1; ack_recvd = 1; } /* else data is wrong, ignore */ } return data_len; }
int main(int argc, char *argv[]) { int sockfd; struct addrinfo hints, *servinfo, *p; int rv; socklen_t addr_len; struct sockaddr_storage their_addr; char buf[MAXBUFLEN]; int cs_pass; //return value for checksum validation int packets_to_send = 0; //how many message packets remain to send to the server int packets_sent = 0; //how many total message packets we have sent int acks_outstanding = 0; //the number of outstanding ACKs in the window int current_seq_no = 0; //the value of the next packet sequence number int duplicateAck = 0; struct timeval current_tv; struct packet pkt; //packet struct for manipulating packet data char* pkt_string; //packets are encoded as strings before sending struct packet window[WINDOW_SIZE]; //holds packet data for packets waiting to be ACKd int window_slot_full[WINDOW_SIZE]; //holds the seq_no for the packet in that slot in the window, 0 if empty //initialize the window as having no contained packets for(int i = 0; i < WINDOW_SIZE; i++) window_slot_full[i] = 0; //arg check if (argc != 3) { fprintf(stderr,"usage: %s hostname message\n", argv[0]); exit(1); } //setup for getaddrinfo memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; //get info about the server if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; } // loop through all the results and make a socket for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("client: socket"); continue; } break; } if (p == NULL) { fprintf(stderr, "client: failed to bind socket\n"); return 2; } fd_set readfds; struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; FD_ZERO(&readfds); FD_SET(sockfd, &readfds); //send a SYN packet to the server to signal we want to open a connection pkt.syn = 1; pkt.ack = 0; pkt.fin = 0; pkt.seq_no = current_seq_no++; pkt.msg = '~'; //encode the SYN packet as a string pkt_string = create_packet_string(pkt); //send the SYN to the server rv = unreliable_sendto(sockfd, pkt_string, strlen(pkt_string), 0, p->ai_addr, p->ai_addrlen); if(rv == -1){ perror("client: unreliable_sendto"); } bzero(buf, MAXBUFLEN); addr_len = sizeof(their_addr); //wait for something to read while(select(sockfd+1, &readfds, NULL, NULL, &timeout) == 0){ //resend the packet rv = unreliable_sendto(sockfd, pkt_string, strlen(pkt_string), 0, p->ai_addr, p->ai_addrlen); if(rv == -1){ perror("client: unreliable_sendto"); } //wait again //the file descriptor list was getting reset for some reason FD_ZERO(&readfds); FD_SET(sockfd, &readfds); timeout.tv_sec = 1; timeout.tv_usec = 0; } free(pkt_string); //listen for an ACK to our SYN recvfrom(sockfd, buf, MAXBUFLEN-1, 0, (struct sockaddr*) &their_addr, &addr_len); printf("RECV: %s\n", buf); struct packet resp; //convert the received message into a packet struct //cs_pass is the result of the checksum validation cs_pass = convert_to_packet(buf, &resp); if(resp.ack){ printf("Received ACK for seq_no %d %s\n", resp.seq_no, cs_pass ? "" : "(checksum failed)"); } packets_to_send = strlen(argv[2]); //while we still have packets to send, or are waiting for acks, go into the send/receive loop while(packets_sent < packets_to_send || acks_outstanding > 0) { //sliding window -- if there is room in the window, send packets while(acks_outstanding < WINDOW_SIZE){ //however, don't send packets if the message has all been sent, and we are waiting for acks only if(packets_sent == packets_to_send) break; //construct the packet pkt.syn = 0; pkt.ack = 0; pkt.fin = 0; pkt.seq_no = current_seq_no++; pkt.msg = argv[2][packets_sent]; //find the next available open slot in the window int j = 0; while(window_slot_full[j] && j < WINDOW_SIZE) j++; //encode the packet as a string pkt_string = create_packet_string(pkt); printf("packet string: %s (window slot = %d)\n", pkt_string, j); //copy the packet into the window memcpy(&window[j] , &pkt, sizeof(pkt)); //mark the window as being full there window_slot_full[j] = pkt.seq_no; //send the packer rv = unreliable_sendto(sockfd, pkt_string, strlen(pkt_string), 0, p->ai_addr, p->ai_addrlen); if(rv == -1){ perror("client: unreliable_sendto"); } gettimeofday(&(window[j].tv), NULL); packets_sent++; acks_outstanding++; free(pkt_string); } //the file descriptor list was getting reset for some reason FD_ZERO(&readfds); FD_SET(sockfd, &readfds); timeout.tv_sec = 1; timeout.tv_usec = 0; //listen for incoming packets rv = select(sockfd+1, &readfds, NULL, NULL, &timeout); gettimeofday(¤t_tv, NULL); if(rv){ bzero(buf, MAXBUFLEN); addr_len = sizeof(their_addr); //receive a packet recvfrom(sockfd, buf, MAXBUFLEN-1, 0, (struct sockaddr*) &their_addr, &addr_len); printf("RECV: %s\n", buf); //convert packet to a string //cs_pass is a boolean indicator of the checksum valiation cs_pass = convert_to_packet(buf, &resp); //if the response is an ACK if(resp.ack && cs_pass){ int ack_matched = 0; //window_slot_full array contains the seq_no of the packet in that window slot for(int i = 0; i < WINDOW_SIZE; i++){ //if this slot matches the ACK we just received if(window_slot_full[i] == resp.seq_no){ printf("ACK received for seq no %d, window slot %d\n", resp.seq_no, i); ack_matched = 1; //empty the window slot window_slot_full[i] = 0; bzero(&window[i], sizeof(struct packet)); acks_outstanding--; } //if this slot has a seq_no less than the ACK we just received //the old ack was lost and should be removed due to cumulative ACKs if(window_slot_full[i] < resp.seq_no && window_slot_full[i] > 0){ printf("cumulative ACK for seq no %d, window slot %d\n", window_slot_full[i], i); ack_matched = 1; //empty the window slot window_slot_full[i] = 0; bzero(&window[i], sizeof(struct packet)); acks_outstanding--; } } //if we got an ACK we weren't waiting for, it was a duplicate if(ack_matched == 0){ printf("ACK with seq_no %d received, but is not outstanding!\n", resp.seq_no); duplicateAck++; //after 3 duplicates, resend everything in the window if(duplicateAck == 3){ duplicateAck = 0; printf("too many duplicate acks received, resending all in window\n"); for(int i = 0; i < WINDOW_SIZE; i++){ if(window_slot_full[i]){ printf("\t resend packet with seq_no %d\n", window_slot_full[i]); pkt_string = create_packet_string(window[i]); rv = unreliable_sendto(sockfd, pkt_string, strlen(pkt_string), 0, p->ai_addr, p->ai_addrlen); if(rv == -1){ perror("client: unreliable_sendto"); } gettimeofday(&(window[i].tv), NULL); free(pkt_string); } } } } } } //the select() call timed out, resend all packets else{ printf("client timeout on receiving\n"); for(int i = 0; i < WINDOW_SIZE; i++){ if(window_slot_full[i]){ printf("\t resend packet with seq_no %d\n", window_slot_full[i]); pkt_string = create_packet_string(window[i]); rv = unreliable_sendto(sockfd, pkt_string, strlen(pkt_string), 0, p->ai_addr, p->ai_addrlen); if(rv == -1){ perror("client: unreliable_sendto"); } gettimeofday(&(window[i].tv), NULL); free(pkt_string); } } } for(int i = 0; i < WINDOW_SIZE; i++){ if(window_slot_full[i]){ if(current_tv.tv_sec - window[i].tv.tv_sec > 2) printf("packet with seq no %d has timed out\n", window[i].seq_no); } } } int fin_seq_no = current_seq_no++; //send our FIN pkt.syn = 0; pkt.ack = 0; pkt.fin = 1; pkt.seq_no = fin_seq_no; pkt.msg = '~'; pkt_string = create_packet_string(pkt); rv = unreliable_sendto(sockfd, pkt_string, strlen(pkt_string), 0, p->ai_addr, p->ai_addrlen); if(rv == -1){ perror("client: unreliable_sendto"); } timeout.tv_sec = 1; timeout.tv_usec = 0; //wait for a response int connection_finished = 0; while(!connection_finished){ while(select(sockfd+1, &readfds, NULL, NULL, &timeout) == 0) { //resend the packet rv = unreliable_sendto(sockfd, pkt_string, strlen(pkt_string), 0, p->ai_addr, p->ai_addrlen); if(rv == -1){ perror("client: unreliable_sendto"); } //wait again //the file descriptor list was getting reset for some reason FD_ZERO(&readfds); FD_SET(sockfd, &readfds); timeout.tv_sec = 1; timeout.tv_usec = 0; } bzero(buf, MAXBUFLEN); addr_len = sizeof(their_addr); recvfrom(sockfd, buf, MAXBUFLEN-1, 0, (struct sockaddr*) &their_addr, &addr_len); printf("RECV: %s\n", buf); cs_pass = convert_to_packet(buf, &resp); if(resp.ack){ printf("Received ACK for seq_no %d %s\n", resp.seq_no, cs_pass ? "" : "(checksum failed)"); } if(resp.seq_no == fin_seq_no) connection_finished = 1; } freeaddrinfo(servinfo); close(sockfd); return 0; }