/* * 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 #include using namespace std; 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; game(uint16_t ip, uint16_t sl, string d) : initial_points(ip), stat_limit(sl), description(d) {} bool send(int skt){ description_length = description.length(); if(7 != write(skt, this, 7)) return false; if(description_length != write(skt, description.c_str(), description_length)) return false; 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 */ 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); 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 our_version.send(client_fd); our_game.send(client_fd); // Read a character and flags /* * This next part of the demo shows two ways of retrieving the values of the character flags. * Setup: * A character message is variable in size, and certainly bigger than 34 bytes, but in * this demo, we'll just be looking at the flags variable. That's the 34th byte, which * will be in character_beginning[33] after the read. The rest of the character is left * in the pipe when the connection is closed, and never read. So don't think of this as * a complete character reading solution! The flags are a total of 8 bits, and they're * copied to a uint8_t variable "flags" for convience. * * Method 1: Use bitwise and (&) * In this case, we use a bitwise and along with a number that has a 1 in the place where * the flag we're interested in is located, and 0's everywhere else. Doing a bitwise and * would look like this to read join battle: * flags: ???? ???? (? because we don't know any of the values) * join battle: 0100 0000 In hex, this is 0x40 * Result with &: 0?00 0000 Now join_battle is the only bit that's the original ? * So if the result is 0x40, join_battle is set. If it's 0, join_battle isn't set * In C, 0x40 (and all other non-0 numbers) are true, and 0 is false * So this'll let us know if join_battle is true: * if(flags & 0x40) * // join battle was set * else * // join battle wasn't set * If you go with this method, I recomment defining values for alive, join battle, etc. * Like #define ALIVE 0x80, #define JOIN_BATTLE 0x40, etc. * * Method 2: Line up a struct with limited-length integers * To do this, we set up a struct that'll match the flags. struct flag_byte. Members have * to be from lowest bit to highest bit, so we start with reserved, and end with alive. We * set these fields to uint8_t, but limit the length to 1 bit, or 3 in the case of reserved. * Then, we make a struct of that type, and copy the value of flags to it. I think in C * you could probably just do this: * fb = (struct flag_byte)flags; * C++ wasn't having any of it, claims they're not compatible types. So I used memcpy to * bypass the type mismatch and just copy the byte over. You wouldn't have to do this if * you read the struct directly out of the network socket. This method should work just fine * with a C++ class, using a read method to match the send from the game and version structs. */ uint8_t character_beginning[34]; read(client_fd, character_beginning, 34); if(character_beginning[0] != 10){ /* We expect that the client will send us a character first thing. If that doesn't happen, * then we just give up on the client and disconnect */ printf("Something is wrong, we were sent a type %d\n", character_beginning[0]); // After this we'll hit close(client_fd) after the else } else { // Might as well print the name, since we have it. It'll be 1 byte in, right after the type printf("Name: %s\n", character_beginning + 1); // The next line is just so we don't have to keep typing character_beginning[33] uint8_t flags = character_beginning[33]; printf("Flags: %x\n", flags); /* This method, with the "if", works fine, but takes four lines */ if(flags & 0x80) printf("Alive: True\n"); else printf("Alive: False\n"); /* Using the ternary operator ?: allows us to shrink down to one line. It has the same effect * as the solution above, although it does use the string placeholder with printf instead of * keeping two strings. I suppose if you wanted it to do really exactly the same thing, you could * write this: * printf((flags & 0x80)? "Alive: True\n":"Alive: False\n"); */ printf("Join Battle: %s\n", (flags & 0x40)? "True":"False"); printf("Monster: %s\n", (flags & 0x20)? "True":"False"); printf("Started: %s\n", (flags & 0x10)? "True":"False"); printf("Ready: %s\n", (flags & 0x08)? "True":"False"); /* The bit sizes of these fields must total 8, same as our uint8_t */ struct flag_byte { uint8_t reserved : 3; uint8_t ready : 1; uint8_t started : 1; uint8_t monster : 1; uint8_t join_battle : 1; uint8_t alive : 1; }; // This works fine without being packed, but it wouldn't be a bad idea printf("sizeof(struct flag_byte) = %lu\n", sizeof(struct flag_byte)); // size is 1 byte struct flag_byte fb; memcpy(&fb, &flags, 1); // Because C++ didn't like fb = flags; if(fb.alive) // Same as above, we just use fb.alive instead of flags & 0x80 printf("Alive: True\n"); else printf("Alive: False\n"); printf("Join Battle: %s\n", (fb.join_battle)? "True":"False"); printf("Monster: %s\n", (fb.monster)? "True":"False"); printf("Started: %s\n", (fb.started)? "True":"False"); printf("Ready: %s\n", (fb.ready)? "True":"False"); } close(client_fd); } return 0; }