/* * This is a better server than simple_server.c * I'm only going to comment changes from simple_server.c * So if there's something that doesn't make any sense in here, maybe look at that one. */ #include #include #include #include #include #include #include #include #include using namespace std; /* Sending method: Make a class that lines up with the lurk message, and just read the * lurk message directly into it. * Pros: * Clean implementation, doesn't require pointer arithmetic * Makes for an easy place to put the implementation * We could have a receive method to match send, and then this could be used either direction * Minimizes calls to write * Cons: * Doesn't work in C (although you could just make a function to send/receive structs) * Caveats: * Properties of the class have to line up with the protocol message. So they go first. * Requires packed classes. Otherwise padding will throw off receive */ class version { public: // You don't have to use uint8_t, it's the same as "unsigned char" uint8_t type = 14; uint8_t major; uint8_t minor; uint16_t extension_length = 0; version(uint8_t p_major, uint8_t p_minor) : major(p_major), minor(p_minor) {} bool send(int skt){ return 5 == write(skt, this, 5); } } __attribute__((packed)); // we'll have to make sure this is packed class game { public: uint8_t type = 11; uint16_t initial_points; uint16_t stat_limit; uint16_t description_length; string description; // Note: A C++ string object can put the string on the heap game(uint16_t ip, uint16_t sl, string d) : initial_points(ip), stat_limit(sl), description(d) {} bool send(int skt){ /* description_length is set here instead of in the constructor in case somebody makes a * class game, then changes the public property "description", then sends the structure. * In that case, we'd need to update description_length. Remember, by leaving the properties * public, we leave the option for them to be changed later without running any methods. * The solution below handles that just fine. */ description_length = description.length(); if(7 != write(skt, this, 7)) return false; // If we didn't write 7 characters, something went wrong if(description_length != write(skt, description.c_str(), description_length)) return false; // Same idea here, we should write description_length characters return true; } } __attribute__((packed)); /* g++ says it ignores this __attribute__((packed)), but it doesn't * clang++ is more honest, says no such thing, and also doesn't ignore this * Either way, without __attribute__((packed)), some padding will be inserted, * and the structure won't line up with the expected network message. */ int skt; void shut_down_server(int signal){ printf("\nDoing everything we need to do to shut down the server\n"); close(skt); exit(0); } int main(int argc, char ** argv){ uint16_t listen_port = 5141; // 5141 is the default if(argc > 1){ listen_port = atoi(argv[1]); if(listen_port < 1){ printf("Invalid port: %s\n", argv[1]); exit(1); } printf("Will listen on port %d\n", listen_port); } else { printf("No port specified, defaulting to %d\n", listen_port); } struct sigaction sa; sa.sa_handler = shut_down_server; sigaction(SIGINT, &sa, 0); struct sockaddr_in sad; sad.sin_port = htons(listen_port); sad.sin_addr.s_addr = INADDR_ANY; sad.sin_family = AF_INET; skt = socket(AF_INET, SOCK_STREAM, 0); // Step 1 if(skt == -1){ perror("socket"); return 1; } if( bind(skt, (struct sockaddr *)(&sad), sizeof(struct sockaddr_in)) ){ // step 2 perror("bind"); return 1; } if( listen(skt, 5) ){ // step 3 perror("listen"); return 1; } int client_fd; struct sockaddr_in client_address; socklen_t address_size = sizeof(struct sockaddr_in); // May as well just make one instance of each struct for the send routine below version our_version(6,17); game our_game(421, 1048, "An even better game than the last one!"); while(1){ client_fd = accept(skt, (struct sockaddr *)(&client_address), &address_size); // step 4 printf("Connection made from address %s\n", inet_ntoa(client_address.sin_addr)); // send stuff here. Pretty simple since all the complicated bits are already done! our_version.send(client_fd); our_game.send(client_fd); close(client_fd); } return 0; }