// Run like this: simple_client address port // Results in argv ["./simple_client", "address", "port"] #include #include #include #include #include #include #include #include int main(int argc, char ** argv){ if(argc < 3){ printf("Usage: %s hostname port\n", argv[0]); return 1; } struct sockaddr_in sad; sad.sin_port = htons(atoi(argv[2])); sad.sin_family = AF_INET; int skt = socket(AF_INET, SOCK_STREAM, 0); // do a dns lookup struct hostent* entry = gethostbyname(argv[1]); if(!entry){ if(h_errno == HOST_NOT_FOUND){ printf("This is our own message that says the host wasn't found\n"); } herror("gethostbyname"); return 1; } struct in_addr **addr_list = (struct in_addr**)entry->h_addr_list; struct in_addr* c_addr = addr_list[0]; char* ip_string = inet_ntoa(*c_addr); sad.sin_addr = *c_addr; // copy the address we found into sad // Finally done with DNS! printf("Connecting to: %s\n", ip_string); if( connect(skt, (struct sockaddr*)&sad, sizeof(struct sockaddr_in)) ){ perror("connect"); return 1; } // it was simple_client.c until this point char message[128]; uint16_t messagelen; /* First, we'll read the message length. We really can't read the message * before this point, because we don't know how long it was. For this simple * client, we could get away with just reading everything in the pipe, and figure * out if we've got the numbers at the end (moosenum and mooseweight) by * comparing how much we read to messagelen, but this approach will get more * difficult with more complicated protocols. */ if(2 != read(skt, &messagelen, 2)){ // uint16_t = 2 bytes printf("We have some kind of problem\n"); return 1; } // Note: messagelen was sent in host byte order. As long as that's the same // on both the client and server, we're ok, but if not, it won't work right. // Most protocols send in network byte order, then convert to host byte order. // If we want to make sure we're dealing with host byte order, knowing messagelen // was sent in little endian, we could call le16toh, like this: messagelen = le16toh(messagelen); // Note that le16toh does nothing at all on x86 uint16_t expected_total = messagelen + sizeof(int) + sizeof(double); /* This way works, but there is an easier way (use recv and MSG_WAITALL) ssize_t actual_length = read(skt, message, 128); while(actual_length < expected_total) actual_length += read(skt, message + actual_length, 128 - actual_length); */ /* This works, but what a mess! ssize_t actual_length = recv(skt, message, expected_total, MSG_WAITALL); printf("Received message(%ld bytes): %s\n", actual_length, message); printf("moosenum = %d\n", *(int*)(message + messagelen)); printf("mooseweight = %lf\n", *(double*)(message + messagelen + sizeof(int))); */ // One way: Meter everything on the way out if(127 > messagelen) { recv(skt, message, messagelen, MSG_WAITALL); message[messagelen] = 0; printf("The message: %s\n", message); } else { printf("Oh no, we don't have enough space to read the message!\n"); } /* This is more of the meter everything way. It works, but the struct approach * below is a lot cleaner, though not really shorter */ /* int32_t moosenum; recv(skt, &moosenum, 4, MSG_WAITALL); printf("moosenum = %d\n", moosenum); double mooseweight; recv(skt, &mooseweight, 8, MSG_WAITALL); printf("mooseweight = %lf\n", mooseweight); */ /* One more way. This reduces the number of times we call recv, which is a system call * It also packs the data into a struct for us, which is neater that having a bunch of * individual variables. You don't *have* to do it this way. It'll work fine with * either of the approaches above. In fact, the first one, with the pointer arithmetic * where everything was read into message, will probably run fastest. */ struct moosedata { int32_t moosenum; double mooseweight; }__attribute__((packed)); // Otherwise we'll end up with padding in the structure struct moosedata md; printf("sizeof(struct moosedata) = %lu\n", sizeof(struct moosedata)); recv(skt, &md, sizeof(md), MSG_WAITALL); printf("moosenum = %d\n", md.moosenum); printf("mooseweight = %lf\n", md.mooseweight); close(skt); return 0; }