/* * Simplest server I can write short of removing all error detection */ /* This next block of includes is going to be pretty normal for clients and servers. * You can get away with a couple less sometimes, but I'd just add all 4 without thinking much */ #include #include #include #include // Not server specific #include // for printf #include // for read and write // Main doesn't actually use argc and argv // It would be nice if we could use one of these to specify which port to listen on // Look at better_server.c for an example there int main(int argc, char ** argv){ /* Documentation for struct sockaddr_in can be found in the manual, like this: * man 7 ip * A little way down the manual, it'll explain what's in a sockaddr_in * A sockaddr_in represents an IP address AND port, together * _in on the end is for inet, or IPv4. sockaddr_in6 is IPv6 */ struct sockaddr_in sad; /* htons will convert from host byte order to network byte order * As in, it swaps the first and second byte of the unsigned short it's given * unsigned short is two bytes long * The server will work on big endian architecture, like MIPS or whatnot, without htons * On big endian architectures, it doesn't hurt to call htons, because it'll be defined * something like this for big endian * #define htons(X) X * On little endian, it actually does the conversion. x86 is little-endian * Most networking protocols use big-endian, but LURK doesn't. */ sad.sin_port = htons(5141); sad.sin_addr.s_addr = INADDR_ANY; sad.sin_family = AF_INET; /* What's a socket? * A socket is an object that represents and end of a network connection * It has both an IP address and a port * You can use one for either a client or a server * If you're writing a server, use bind, listen, and then accept on the socket * If you're writing a client, call connect (only) in the socket * The OS will deallocate it once your program is done, but it might take a minute! * The "socket" function will create a socket for us * The int that results is used to track our socket, and can be used as a file descriptor * AF_INET = IPv4 * SOCK_STREAM = TCP */ int skt = socket(AF_INET, SOCK_STREAM, 0); // Step 1 // If we couldn't create a socket for some reason, we might as well give up here! if(skt == -1){ perror("socket"); return 1; } /* bind will give our socket an address and port. That's needed before we can listen * Only servers call bind * The first parameter is the socket to bind * Next is the address structure, BUT it has to be a struct sockaddr * * bind can handle many types of address (for example, IPv4 and IPv6), so the exact type will vary * That's why we also have to pass the size. struct sockaddr_in6 is bigger than struct sockaddr_in */ if( bind(skt, (struct sockaddr *)(&sad), sizeof(struct sockaddr_in)) ){ // step 2 // Usual reason to fail is because the port is already in use // If you just ran this, wait 30 seconds, or change to a different port perror("bind"); return 1; } /* Start listening for incoming connections * 5 here is a queue limit. We can queue up 5 connections before calling accept * It doesn't limit us to 5 client! connections we've accepted don't count in this limit */ if( listen(skt, 5) ){ // step 3 perror("listen"); return 1; } /* When a client connects, we'll get a special file descriptor to communicate with that client * Servers don't use the socket file descriptor (int skt, above) to communicate with clients. */ int client_fd; /* The client address part is optional. If we pass a 0 to accept instead of a pointer to * this structure, everything will be fine, we just won't know the address of the client * who just connected. */ struct sockaddr_in client_address; socklen_t address_size = sizeof(struct sockaddr_in); // Again, struct sockaddr_in6 would be bigger client_fd = accept(skt, (struct sockaddr *)(&client_address), &address_size); // step 4 /* Now we can communicate with the client using client_fd * Options for functions we could call on client_fd (all have man pages): * read: Waits for a message from the client * recv: Same as read, but has a flags parameter * write: Sends a message to the client * send: Same as write, but has a flags parameter * Note that the last parameter on write is the number of bytes that will be sent * No more, no less. If the string was too short, it'll send whatever was in memory there * If the string is too long, it won't all be sent */ write(client_fd, "Good Morning", 13); /* We could print the address with %u or %x, and just print it as a number * But that would look weird, for an IPv4 address * inet_ntoa will convert an IPv4 address to the familiar notation */ printf("Connection made from address %s\n", inet_ntoa(client_address.sin_addr)); /* * Initiates a TCP disconnect. We're done with the client after this! */ close(client_fd); return 0; }