int main (){ int maxfd, listenfd, connfd, nread, i; char c = 'C'; char temp; char *after; unsigned short block_crc; unsigned char char_block, crc_first, crc_second; char *dirname = "filestore"; struct client *top = malloc(sizeof (struct client)); top->fd = -1; top->next = NULL; struct client *p; fd_set allset; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(PF_INET, SOCK_STREAM, 0); memset(&servaddr, '\0', sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = INADDR_ANY; servaddr.sin_port = htons(PORT); Bind (listenfd, (struct sockaddr *) &servaddr, sizeof (servaddr)); Listen (listenfd, LISTENQ); while(1){ fprintf(stderr, "Time to Select\n"); maxfd = listenfd; FD_ZERO (&allset); FD_SET(listenfd, &allset); //loop through the linked list of clients to refresh the allset for (p = top; p->fd >=0; p = p->next){ FD_SET (p->fd, &allset); if (p->fd > maxfd) maxfd = p->fd; //if the fd is larger than the maxfd, change maxfd. } Select(maxfd+1, &allset, NULL, NULL, NULL); //loop through the linked list until the fd corresponding to the client that is set is found for (p = top; p->fd >=0; p = p->next){ if (FD_ISSET(p->fd, &allset)) break; } //if it is our listening socket then a new client has come if (FD_ISSET(listenfd, &allset)){ newconnection(listenfd, &top); } // otherwise its one of our old clients else if(!p){ //if p is null, we have a problem fprintf(stderr,"uhoh\n"); exit(1); } // if p exists, then we go through the states else { if(p){ if (p->state == initial){ fprintf(stderr, "initial reading from client\n"); // read as many as you can up to 20 characters, leaving off where it last wrote nread = read(p->fd, &(p->buf[p->inbuf]), sizeof(char)*(20 - p->inbuf)); if(nread<0){ perror("read"); removeclient(p->fd, &top); } //use inbuf as an index of where to write next, and how much more can be written p->inbuf = p->inbuf + nread; //transfer stuff in buf to filename until a network newline is reached for (i = 0; i < 20; i++){ p->filename[i] = p->buf[i]; if (p->buf[i] == '\r'){ //once the network newline is found p->filename[i] = '\0';//place a null character to end the string p->state = pre_block; //change states p->fp = open_file_in_dir(p->filename, dirname); //open a file p->inbuf = 0;// reset inbuf to be 0, going to write over buf from 0 index if (write(p->fd, &c, 1)<0){ //send 'C' to client perror("write"); removeclient(p->fd, &top); } break; } } //if the network newline is not found in the 20 characters sent by client, error in filename, drop client if (p->inbuf == 20){ fprintf(stderr, "filename was not found. filename must be less than 20 characters\n"); removeclient(p->fd, &top); } } if (p->state == pre_block){ fprintf(stderr, "pre_block readering from client \n"); nread = read(p->fd, &temp, 1); //read a single character if(nread<0){ //if there was a problem with nread then drop the client perror("read"); removeclient(p->fd, &top); } if (temp== EOT){ temp = ACK; if (write(p->fd, &temp, 1)<0){ perror("write"); removeclient(p->fd, &top); } fprintf(stderr, "finished\n"); removeclient(p->fd, &top); } if (temp == SOH){ p->blocksize = 132; p->state = get_block; } if (temp == STX){ p->blocksize = 1028; p->state = get_block; } } if (p->state == get_block){ fprintf(stderr, "get_block readering from client \n"); /* reads into the buffer as much as it can upto the blocksize of the client * and continues writing where it left off*/ nread = read(p->fd, &(p->buf[p->inbuf]), p->blocksize - p->inbuf); if(nread < 0){ perror("read"); removeclient(p->fd, &top); } p->inbuf = p->inbuf + nread; //once the entire block is received, go to the next state; if (p->inbuf == p->blocksize) p->state = check_block; } if (p->state == check_block){ fprintf(stderr, "checking_block client \n"); char_block = p->current_block; /*removes client if block number and inverse don't match or block number is not * what was expected. however if the blocknum is a previously received block num, send ack*/ if (255 - p->buf[0] != p->buf[1]){ fprintf(stderr, "block number and inverse do not match\n"); removeclient(p->fd, &top); } else if (char_block > p->buf[0]){ temp = ACK; if(write(p->fd, &temp, 1)<0){ perror("write"); removeclient(p->fd, &top); } } else if (char_block != p->buf[0]){ fprintf(stderr, "char_block is not correct\n"); removeclient(p->fd, &top); } //otherwise, need to check crc else{ block_crc = crc_message (XMODEM_KEY, &(p->buf[2]), p->blocksize - 4); crc_first = block_crc>>8; crc_second = block_crc; if ((crc_first != p->buf[p->blocksize -2]) || (crc_second != p->buf[p->blocksize -1])){ fprintf(stderr, "crc does not match \n"); temp = NAK; if(write(p->fd, &temp, 1) < 0){ perror("write"); removeclient(p->fd, &top); } } else{ temp = ACK; fprintf(stderr, "writing to client ACK\n"); if (write (p->fd, &temp, 1)<0){ perror("write"); removeclient(p->fd, &top); } if(fwrite(&(p->buf[2]), p->blocksize-4, 1, p->fp)<0){ perror("write"); exit(1); } p->state = pre_block; p->current_block ++; p->inbuf = 0; if(p->current_block > 255) p->current_block = 1; } } } } } }
int main(int argc, char* argv[]) { int soc, len, err; unsigned char buf[128]; // payloads char temp; struct addrinfo *info, hints; struct sockaddr_in peer; memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; if ( argc != 4) { fprintf(stderr, "Usage: %s <hostname> <port> <filename>\n", argv[0]); exit(1); } if ((err = getaddrinfo(argv[1], argv[2], &hints, &info))) { fprintf (stderr, "%s\n", gai_strerror(err)); exit (1); } FILE *fp = fopen(argv[3], "rb"); if (!fp) { perror("fopen"); exit(2); } peer = *(struct sockaddr_in *)(info->ai_addr); if ((soc = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror ("socket"); exit (1); } if (connect(soc, (struct sockaddr *)&peer, sizeof(peer)) == -1) { perror ("connect"); exit (1); } int state = initial; int current_block, readblock; while (1) { if (state == initial) { write(soc, argv[3], strlen(argv[3])); write(soc, "\r\n", 2); current_block = 1; readblock = 1; state = handshake; printf("Client sent filename and entering handshake.\n"); } if (state == handshake) { while ((len = read(soc, &temp, 1)) > 0) { if (temp == 'C') { printf("Client found C; entering send_block\n"); state = send_block; break; } } if (len <= 0) { fprintf(stderr, "Server dropped client\n"); exit(1); } } if (state == send_block) { if (readblock) { readblock = 0; len = fread(buf, 1, 128, fp); if (len == 0) { printf("Client got 0 fread; moving to finish.\n"); state = finish; continue; } while (len < 128) buf[len++] = SUB; } unsigned char char_block = current_block; temp = SOH; printf("Client sending the block+overhead\n"); write(soc, &temp, 1); printf("Sending block %d and inverse %d\n", char_block, 255-char_block); write(soc, &char_block, 1); char_block = 255 - char_block; write(soc, &char_block, 1); write(soc, buf, 128); unsigned short crc = crc_message(XMODEM_KEY, buf, 128); printf("Client wants to send crc %x\n", crc); char_block = crc >> 8; printf("Client sends crc first byte %x\n", char_block); write(soc, &char_block, 1); char_block = crc; printf("Client sends crc second byte %x\n", char_block); write(soc, &char_block, 1); printf("Client moving to wait_reply\n"); state = wait_reply; } if (state == wait_reply) { while ((len = read(soc, &temp, 1)) > 0) { if (temp == NAK) { printf("Client received nak; moving back to send_block\n"); state = send_block; break; } if (temp == ACK) { printf("Client received ack; moving to send_block for next block\n"); state = send_block; readblock = 1; current_block++; if (current_block > 255) current_block = 0; break; } } } if (state == finish) { temp = EOT; printf("Client sent eot, waiting for ack\n"); write(soc, &temp, 1); while ((len = read(soc, &temp, 1)) > 0) { if (temp == NAK) { temp = EOT; write(soc, &temp, 1); printf("Client received nak to its EOT! Sending new EOT\n"); break; } if (temp == ACK) { printf("Done!\n"); exit(0); } } } } return 0; }
int main() { int listenfd, clientfd, maxfd; fd_set cset, rset; Client *clients = NULL; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; int one = 1; unsigned char temp_block_num, temp_block_inverse; unsigned short temp_crc, block_crc; char temp = 'C'; /*If a client disconnects while we are writing an ACK/NAK to them it sends * a SIGPIPE which needs to be taken care of */ signal(SIGPIPE, SIG_IGN); //Setup the main listening socket listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); if ((setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int))) == -1) { perror("setsockopt"); } Bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); Listen(listenfd, 5); FD_ZERO(&cset); FD_SET(listenfd, &cset); maxfd = listenfd; while (1) { /* * Accept any clients that want to connect. */ rset = cset; Select(maxfd + 1, &rset, NULL, NULL, 0); // Check for new clients. if (FD_ISSET(listenfd, &rset)) { printf("New Client Connected\n"); Client *temp = Malloc(sizeof(Client)); clilen = sizeof(cliaddr); clientfd = Accept(listenfd, (struct sockaddr *) &cliaddr, &clilen); FD_SET(clientfd, &cset); if (clientfd > maxfd) { maxfd = clientfd; } // Populate the client struct with default starter data. temp->fd = clientfd; temp->inbuf = 0; temp->state = initial; temp->next = start; temp->prev = NULL; if (start) { start->prev = temp; } start = temp; continue; } /* * Loop through all the clients in the linked list, * checking their file descriptors for activity. */ clients = start; while (clients) { clientfd = clients->fd; if (FD_ISSET(clientfd, &rset)) { /* * Using switch statement for states is too messy in terms of proper flow control * so just using if statements instead; they're cleaner and easier to follow. */ if (clients->state == initial) { // Read name of the file being sent. clients->inbuf += Read(clientfd, &(clients->filename)[clients->inbuf], 20 - clients->inbuf); if (find_network_newline(clients->filename, clients->inbuf) > 0) { clients->filename[clients->inbuf - 2] = '\0'; // Open/create a file with the name. clients->fp = open_file_in_dir(clients->filename, FILE_DIR); // Assign defaults to client struct. clients->state = pre_block; clients->inbuf = 0; clients->current_block = 1; // Let the client know that the server is ready for the data. temp = 'C'; Write(clientfd, &temp, 1); } } if (clients->state == pre_block) { // Find out what state the client is in. Read(clientfd, (clients->buf), 1); switch (clients->buf[0]) { case EOT: clients->state = finished; break; case SOH: clients->state = get_block; clients->blocksize = 132; break; case STX: clients->state = get_block; clients->blocksize = 1028; break; default: temp = NAK; Write(clientfd, &temp, 1); } } if (clients->state == get_block) { // Keep getting more of the payload till you have the required minimum amount. clients->inbuf += Read(clientfd, (clients->buf) + (clients->inbuf), clients->blocksize - clients->inbuf); if (clients->inbuf == clients->blocksize) { clients->state = check_block; } } if (clients->state == check_block) { // Fetch block numbers from the packet. temp_block_num = (clients->buf)[0]; temp_block_inverse = (clients->buf)[1]; // Make sure the block numbers are correct - drop the client if they're not. if ((temp_block_num != (255 - temp_block_inverse) || (temp_block_num > clients->current_block))) { FD_CLR(clientfd, &cset); clients = drop_client(clients); /* * Since drop_client gives us the client after the one we dropped, * we use continue to go back to start of the loop. */ break; } // Fetch and assemble the CRC given by client for the payload recieved. temp_crc = 0; temp_crc = (unsigned char)(clients->buf)[(clients->blocksize) - 2]; temp_crc <<= 8; temp_crc |= (unsigned char)((clients->buf)[(clients->blocksize) - 1]); // Calculate the actual CRC for the payload recieved. block_crc = crc_message(XMODEM_KEY, (unsigned char *)((clients->buf) + 2), (clients->blocksize) - 4); // Compare the given and calculated CRC to ensure payload integrity. if ((temp_block_num == clients->current_block) && (temp_crc == block_crc)) { printf("File: %s \t Block:%d Successfully recieved\n", clients->filename, clients->current_block); // Write the payload to file if CRCs match if (fwrite((clients->buf) + 2, 1, ((clients->blocksize) - 4), clients->fp) == 0) { perror("Write to File error"); exit(-1); } // Send the client an ACK temp = ACK; Write(clientfd, &temp, 1); // Increment service side block number counter and prepare for next block. clients->state = pre_block; clients->current_block += 1; // Deal w/ wrap around when block number hits 255. if (clients->current_block == 256) { clients->current_block = 0; } // Reset the buffer position. clients->inbuf = 0; } else if (temp_crc != block_crc) { printf("File: %s \t Block:%d CRC mismatch.\nExpected:%x, Got %x\n", clients->filename, clients->current_block, temp_crc, block_crc); // Sent the client a NAK if the CRCs didn't match and prepare for repeat block. temp = NAK; Write(clientfd, &temp, 1); clients->state = pre_block; clients->inbuf = 0; } else if (temp_block_num < clients->current_block) { // If we got a repeat block, just send the client an ACK. temp = ACK; Write(clientfd, &temp, 1); } } if (clients->state == finished) { printf("File Transfer Finished: %s\n", clients->filename); // Cleanup after the client when transfer is finished. // Send the client a final ACK. temp = ACK; Write(clientfd, &temp, 1); // Remove the client and their socket from our list/set of clients. FD_CLR(clientfd, &cset); clients = drop_client(clients); } } // Move on to the next client in the list (if any). if (clients) { clients = clients->next; } } } }
/* A helper function for processing a client. This functions covers all states except the finished state. This function takes in the argument toplist (the top of the linked list, and cl, the current client node/struct. */ int processclient(struct client *cl) { /* Variable used for writing ACK, NAK, and other exciting control codes */ char acker; switch (cl->state) { /* If client is in the initial state */ case initial: { /* Variable to determine if network newline has been found */ int readin = 0; /* Read in one byte at a time so that the read() call doesn't block, top when network newline has been found and add a null terminator at the end of the filename. */ memset(cl->filename, 0, 20); if (read(cl->fd, &cl->filename, 20) <= 0) { /*XXX*/ return 0; } printf("%s:%d--->filename: %s\n", __func__, __LINE__, cl->filename); readin = 1; /* If filename is completely read in, use helper.c to generate the file pointer for it, reset the buffer index, and transition to pre_block state */ if ( readin == 1) { cl->fp = open_file_in_dir(cl->filename, "storage"); cl->inbuf = 0; cl->state = pre_block; } /* Write "C" to the client so it can move on with its transfer, if write fails, drop client by transitioning to finished block */ if ( write(cl->fd, "C", 1) < 1) { perror("Writing C failed"); cl->state = finished; } } break; /* If client is in the pre_block state */ case pre_block: { /* Temporary buffer to hold in any incoming control codes. 2048 is overkill, yes, however it can't hurt to be safe. */ char prebuf[2048]; /* Read in one byte at a time from the client */ if (read(cl->fd, &prebuf[0], 1) == 1) { printf("*"); /* If EOT is received, drop client by transitioning to finish state */ if (prebuf[cl->inbuf] == EOT) { /* Close file pointer */ fclose(cl->fp); /* Set acker to ACK control code and write it to the client */ acker = ACK; printf("receiver successful\n"); if (write(cl->fd, &acker, sizeof(char)) < 1) { perror("writing ACK for EOT in pre_block failed"); } cl->state = finished; } /* If SOH block is received, set the expected blocksize on client to 128, and transition to get_block */ if (prebuf[0] == SOH) { cl->blocksize = 128; cl->state = get_block; } /* If STX block is received, set the expected blocksize on client to 1024, and transition to get_block */ if (prebuf[0] == STX) { cl->blocksize = 1024; cl->state = get_block; } } else { perror("pre_block read fail"); } } break; /* If client is in the get_block state */ case get_block: /* If server is expecting a SOH block */ if (cl->blocksize == 128) { /* Read in one byte for current block number and store it in the client's current_block attribute */ if (read(cl->fd, &cl->current_block, 1) < 1) { perror("reading in current_block failed in SOH, get_block"); } /* Read in one byte for inverse block number and store it in the client's inverse_block attribute */ if (read(cl->fd, &cl->inverse_block, 1) < 1) { perror("reading in inverse_block failed in SOH, get_block"); } /* Read in the actual payload to the client's buf attribute, one byte at a time */ while (cl->inbuf < 128) { if (read(cl->fd, &cl->buf[cl->inbuf], 1) == 1) { cl->inbuf++; } else { perror("get_block_soh_read_failure\n"); } /* Check if blocksize bytes have been read in, if so, caclculate CRC and store it in the client's CRC attributes. Transition to check_block. */ if (cl->inbuf == cl->blocksize) { read(cl->fd, &cl->crca, 1); read(cl->fd, &cl->crcb, 1); cl->state = check_block; } } } /* If server is expecting a STX block */ if (cl->blocksize == 1024 ) { /* Read in one byte for current block number and store it in the client's current_block attribute */ if (read(cl->fd, &cl->current_block, 1) < 1) { perror("reading in current_block failed in STX, get_block"); } /* Read in one byte for inverse block number and store it in the client's inverse_block attribute */ if (read(cl->fd, &cl->inverse_block, 1) < 1) { perror("reading in inverse_block failes in STX, get_block"); } /* Read in the actual payload to the client's buf attribute, one byte at a time */ while (cl->inbuf < 1024) { if ( read(cl->fd, &cl->buf[cl->inbuf], 1) == 1) { cl->inbuf++; } else { perror("get_block_stx_read_failure"); } } /* Check if blocksize bytes have been read in, if so, caclculate CRC and store it in the client's CRC attributes. Transition to check_block. */ if (cl->inbuf == cl->blocksize) { read(cl->fd, &cl->crca, 1); read(cl->fd, &cl->crcb, 1); cl->state = check_block; } } break; case check_block: /* Reset buffer index after previous block */ cl->inbuf = 0; /* If inverse block and current block don't corresond, write ACK and drop client */ if ((255 - cl->inverse_block) != cl->current_block) { /* Set acker to the ACK control code and write it */ acker = ACK; if (write(cl->fd, &acker, sizeof(char)) < 1) { perror("ACK failed to write in check_block for invalid inverse"); } cl->state = finished; } /* If previous block and current block numbers are the same, ACK the second one */ if (cl->previous_block == cl->current_block) { acker = ACK; if (write(cl->fd, &acker, sizeof(char)) < 1) { perror("ACK failed to write in check_block for duplicate blocks"); } } /* If blocks are out of order, write ACK and drop client */ if (cl->current_block != cl->previous_block + 1) { if ((cl->previous_block != 255) && (cl->current_block != 0)) { /* Set acker to the ACK control code and write it */ acker = ACK; if (write(cl->fd, &acker, sizeof(char)) < 1) { perror("ACK failed to write in check_block for invalid block order"); } cl->state = finished; } } /* If CRC's don't match up, send NAK */ if (crc_message(XMODEM_KEY, cl->buf, cl->blocksize) != ((cl->crca << 8) + cl->crcb)) { /* Set acker to NAK and write it */ acker = NAK; if (write(cl->fd, &acker, sizeof(char)) < 1) { perror("NAK failed to write in check_block for invalid CRC"); } } else { /* If none of the above error checks are triggered */ /* Set previous block to current, and account for wrapping at block number 255 */ cl->previous_block = cl->current_block; if ( cl->current_block > 255 ) { cl->current_block = 0; } /* Write the current payload in the client's buf attribute to the file */ if (fwrite(&cl->buf, sizeof(char), cl->blocksize, cl->fp) < cl->blocksize) { perror("Writing payload to file failed"); } /* Set acker to ACK and write it */ acker = ACK; if (write(cl->fd, &acker, sizeof(char)) < 1) { perror("ACK failed to write in check_block file write stage"); } /* Transition back to the pre_block */ cl->state = pre_block; } break; case finished: cl->state = initial; break; default: printf("error status\n"); break; } return 1; }