diff options
Diffstat (limited to 'client.c')
-rw-r--r-- | client.c | 195 |
1 files changed, 119 insertions, 76 deletions
@@ -9,13 +9,9 @@ #include <sys/socket.h> #include <ctype.h> #include <pthread.h> - #include <arpa/inet.h> - #include "protocol.c" -#define MAXDATASIZE 100 // max number of bytes we can get at once - #define MAX_READ_SIZE 1024 #define LINE_MAX 1024 @@ -43,9 +39,9 @@ int main(int argc, char *argv[]) int sockfd, numbytesrecv, numbytessent, recvbytes; // char buf[MAXDATASIZE]; struct addrinfo hints, *servinfo, *p; - int rv; char s[INET6_ADDRSTRLEN]; + // check arugments if (argc != 4) { fprintf(stderr,"<server IP> <server port> <listener port>\n"); exit(1); @@ -58,11 +54,13 @@ int main(int argc, char *argv[]) char* tcpPort = argv[2]; // port we use to connect to server's tcp stream char* udpPort = argv[3]; // port we use to connect to server's udp info and command - if ((rv = getaddrinfo(argv[1], tcpPort, &hints, &servinfo)) != 0) { + // resolve host + int rv; + if ((rv = getaddrinfo(argv[1], tcpPort, &hints, &servinfo)) != 0) + { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; } - // loop through all the results and connect to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, @@ -76,34 +74,31 @@ int main(int argc, char *argv[]) perror("client: connect"); continue; } - break; } - if (p == NULL) { fprintf(stderr, "client: failed to connect\n"); return 2; } - - inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), - s, sizeof s); - // printf("client: connecting to %s\n", s); - + inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s); freeaddrinfo(servinfo); // all done with this structure + // now that we're connectioned, let's do the handshake uint16_t num_stations = handle_handshake(sockfd, udpPort); + // handle_handshake will end the program on fail + // so if we're here, num_stations is correct fflush(stdout); printf("Welcome to Snowcast! The server has %u stations.\n", num_stations); fflush(stdout); + // start the thread to accept command lines pthread_t command_line_thread; pthread_create(&command_line_thread, NULL, command_line_routine, (void*)sockfd); - // CONSIDER: could recieve the welcome message here - // int recvbytes; + // this while loop hangs on recv and runs when there is new data while (1) { - // recv the first byte of the message to get it's type + // get the type of the incoming reply uint8_t reply_type = -1; // print size of utin8 if (recv(sockfd, &reply_type, 1, 0) == -1) { @@ -114,35 +109,38 @@ int main(int argc, char *argv[]) exit(1); } - if (reply_type == 2) { // we have a second welcome message + if (reply_type == WELCOME) { fprintf(stderr, "WECLOME reply received twice. Exiting.\n"); close(sockfd); exit(1); } - if (reply_type == 3) { // we have an announce message - + if (reply_type == ANNOUNCE) { if (!station_is_set) { fprintf(stderr, "ANNOUNCE reply received before SETSTATION command. Exiting.\n"); close(sockfd); exit(1); } - // get the string size + // get the string size for the songname u_int8_t string_size = -1; if (recv(sockfd, &string_size, 1, 0) == -1) { perror("recv in announce"); exit(1); } + + // read the songname char *song_name = malloc(string_size); if(song_name == NULL) { perror("malloc in song name"); } - int bytes_to_read = string_size; if (recv_all(sockfd, song_name, &bytes_to_read) == -1) { perror("recv_all in announce"); exit(1); } + remove_timeout(sockfd); // have received all, so can remove timeout + if (l) printf("received ANNOUNCE reply.\n"); + // print the songname if (!waiting) printf("\n"); // note: this is worth the lines for a clean cmd prompt waiting = 0; fflush(stdout); @@ -151,7 +149,9 @@ int main(int argc, char *argv[]) fflush(stdout); free(song_name); continue; - } else if (reply_type == 4) { // we have an invalid command message + } + + if (reply_type == INVALID) { // we have an invalid command message // get the string size u_int8_t string_size = -1; if (recv(sockfd, &string_size, 1, 0) == -1) { @@ -170,10 +170,15 @@ int main(int argc, char *argv[]) fflush(stdout); free(message); close(sockfd); + + // close the program on all INVALID COMMANDS exit(1); } - else if (reply_type == 6) { // we are getting STATIONINFO - // get the string size + + if (reply_type == STATIONINFO) { // we are getting STATIONINFO + if (l) printf("received STATIONINFO reply.\n"); + + // get the string size, which can be farily long (uint32_t) uint32_t buf_string_size = -1; int bytes_to_read = sizeof(uint32_t); if (recv_all(sockfd, &buf_string_size, &bytes_to_read) == -1) { @@ -182,7 +187,8 @@ int main(int argc, char *argv[]) } uint32_t string_size = ntohl(buf_string_size); - printf("string size: %d\n", string_size); + + // recieve the message char *info = malloc(string_size); if(info == NULL) { perror("malloc in info"); } bytes_to_read = string_size; @@ -190,9 +196,9 @@ int main(int argc, char *argv[]) perror("recv_all 2 in stationinfo"); exit(1); } - remove_timeout(sockfd); - if (l) printf("received STATIONINFO reply.\n"); + remove_timeout(sockfd); // remove the timeout, now that we have the data + // print the info fflush(stdout); printf("Station Information:\n%s\n", info); printf("snowcast_control> "); @@ -201,32 +207,43 @@ int main(int argc, char *argv[]) free(info); continue; } - else if (reply_type == 7) { // we are getting StationShutdown + + if (reply_type == STATIONSHUTDOWN) { // we are getting StationShutdown if (l) printf("received STATIONSHUTDOWN reply.\n"); - if (!waiting) printf("\n"); // note: this is worth the lines for a clean cmd prompt + + if (!waiting) printf("\n"); // note: this is worth the lines for a clean cmd prompt :) waiting = 0; - remove_timeout(sockfd); + + // station no longer set + station_is_set = 0; fflush(stdout); printf("This station has shut down. Please select a different station.\n"); printf("snowcast_control> "); fflush(stdout); continue; } - else if (reply_type == 8) { // we are getting NewStation + + if (reply_type == NEWSTATION) { // we are getting NewStation + if (l) printf("received NEWSTATION reply.\n"); + + // get station number uint16_t station_number = -1; if (recv(sockfd, &station_number, 2, 0) == -1) { perror("recv in new station"); exit(1); } station_number = ntohs(station_number); - if (l) printf("received NEWSTATION reply.\n"); + + // print fflush(stdout); - printf("\nThere is now a new station @ index %d.\n", station_number); + printf("\nThere is now a new station @ index %u.\n", station_number); printf("snowcast_control> "); fflush(stdout); + continue; } + // if we're here, lost conneciton to the server -> end the program printf("\nsocket to server HUNGUP. Exiting.\n"); close(sockfd); exit(1); @@ -235,105 +252,131 @@ int main(int argc, char *argv[]) } void *command_line_routine(void* args) { + // unpack sockfd as arg int sockfd = (int) args; + // buffer for input char input[LINE_MAX]; printf("Enter a number to change to it's station. Enter q to end stream.\n"); printf("snowcast_control> "); fflush(stdout); while (1) { + memset(input, 0, LINE_MAX); char *line = fgets(input, LINE_MAX, stdin); + // nothing was typed if (line == NULL) { continue; - } else if (strncmp("q\n", input, LINE_MAX) == 0) { + } + + // q command: exit the program + if (strncmp("q\n", input, LINE_MAX) == 0) { // end code if type in q exiting = 1; printf("Exiting.\n"); close(sockfd); exit(0); - } else if (strncmp("l\n", input, LINE_MAX) == 0) { + } + + // l command: STATIONINFO command (EXTRA CREDIT) + if (strncmp("l\n", input, LINE_MAX) == 0) { // send the command to list stations if (l) printf("sending LISTSTATIONS command. waiting for STATIONINFO reply.\n"); - apply_timeout(sockfd); + apply_timeout(sockfd); // apply a timeout, will be released when we get the reply int list_station_reply_type = 5; if (send(sockfd, &list_station_reply_type, 1, 0) == -1) { perror("send"); exit(1); } - } else if (strncmp("log\n", input, LINE_MAX) == 0) { + continue; + } + + // log command: toggle logging + if (strncmp("log\n", input, LINE_MAX) == 0) + { l = !l; printf("LOGGING is now %s!\n", l ? "on" : "off"); printf("snowcast_control> "); fflush(stdout); + continue; } - else { - // convert input to an int - int inputInt = atoi(input); - if (input[0] != '0' && inputInt == 0) { - printf("unknown command: %si", input); - printf("snowcast_control> "); - fflush(stdout); - continue; - } - // printf("Changing to station %d.\n", inputInt); - - // set waiting so no new line on announce - waiting = 1; - // send the command to change the station - apply_timeout(sockfd); - struct SetStation setStation; - setStation.commandType = 1; - setStation.stationNumber = htons(inputInt); - int bytes_to_send = sizeof(struct SetStation); - // apply_timeout(sockfd); - if (send_all(sockfd, &setStation, &bytes_to_send) == -1) { - perror("send_all"); - exit(1); - } - if (!station_is_set) { - station_is_set = 1; - } + + // check if this could be a station number + int inputInt = atoi(input); + if (input[0] != '0' && inputInt == 0) { + // if we're in here, it's ~likely~ not a number (ik it's not perfect, sorry :/) + printf("unknown command: %si", input); + printf("snowcast_control> "); + fflush(stdout); + continue; } + + // if we're here, it was a valid number, + // & we need to send SETSTATION command. + if (l) printf("sending SETSTATION command.\n"); + waiting = 1; // just for clean cmd + + // setup struct + struct SetStation setStation; + setStation.commandType = SETSTATION; + setStation.stationNumber = htons(inputInt); + // send it + apply_timeout(sockfd); // apply timeout, will be released when we get ANNOUNCE reply + int bytes_to_send = sizeof(struct SetStation); + if (send_all(sockfd, &setStation, &bytes_to_send) == -1) { + perror("send_all"); + exit(1); + } + + if (!station_is_set) station_is_set = 1; // update, if needed + if (l) printf("waiting for ANNOUNCE reply...\n"); } return (NULL); } uint16_t handle_handshake(int sockfd, char* udp_port) { - apply_timeout(sockfd); // apply timeout for handshake + if (l) printf("found server, sending HELLO command.\n"); - if (l) printf("found server, sending HELLO command. waiting for ANNOUNCE reply.\n"); + apply_timeout(sockfd); // apply timeout for handshake + // after we find connection, we need to send HELLO command struct Hello hello; - hello.commandType = 0; + hello.commandType = HELLO; // convert updPort to an int int udp_port_int = atoi(udp_port); hello.udpPort = htons(udp_port_int); - if (send(sockfd, &hello, sizeof(struct Hello), 0) == -1) { + int hello_len = sizeof(struct Hello); + if (send_all(sockfd, &hello, &hello_len) == -1) { perror("send"); exit(1); } - // recv the first byte of the message to get it's type + if (l) printf("waiting for WELCOME reply...\n"); + + // read WELCOME reply type uint8_t reply_type = -1; // print size of utin8 - if (recv(sockfd, &reply_type, 1, 0) == -1) { + if (recv(sockfd, &reply_type, 1, 0) == -1) { // only one byte, so can use recv perror("recv in handshake"); exit(1); } - // recv the message, check for errors too + if (reply_type != WELCOME) { + fprintf(stderr, "first reply is not WELCOME. Exiting.\n"); + close(sockfd); + exit(1); + } + + // get the number of stations int16_t num_stations = -1; int bytes_to_read = sizeof(uint16_t); if (recv_all(sockfd, &num_stations, &bytes_to_read) == -1) { perror("recv_all in handshake"); exit(1); } - if (l) printf("received ANNOUNCE reply.\n"); - // remove timeout since we no longer are "waiting" for an immediate reply - remove_timeout(sockfd); - return ntohs(num_stations); + remove_timeout(sockfd); // remove timeout since we no longer are "waiting" for an immediate reply + return ntohs(num_stations); // return the num_stations to be printed }
\ No newline at end of file |