/* client.c * Greatly inspired from examples written by tmouse, July 2005 * http://cboard.cprogramming.com/showthread.php?t=67469 * Modified to run multi-platform by Christian Beier . */ #ifndef __MINGW32__ #include #endif #include #include #include #include #include "msock.h" #define MULTICAST_RECV_BUF 10240 #define MULTICAST_SO_RCVBUF 425984 #define DEFAULT_MCAST_IP_STR "ff02::114" #define DEFAULT_PORT_STR "9000" /* Cmdline Arguments */ char *prog_name = NULL; char *mcast_ip = NULL; char *port = NULL; /* Sockets as global, used everywhere, even in die() */ SOCKET mcast_sock = (SOCKET) -1; /* Multicast socket for receiving data */ SOCKET ucast_sock = (SOCKET) -1; /* Unicast socket for give feedback to server */ /* Buffer used for earch recvfrom() */ char recvbuf[MULTICAST_RECV_BUF]; /* Strings to print out representation of various states of the program */ const char * const state_str[] = { "exiting", "wait_hello_and_connect_back", "wait_start_and_start_job", "receive_data", "finalize_job", "is_there_more_job" }; /* Some boring funcs you didn't want to read now */ void die(char* msg); void usage(char *msg); void arg_parse(int argc, char* argv[]); /* Parts of the "protocol", definitions are after main() */ int wait_hello_and_connect_back(); int wait_start_and_start_job(); int receive_data(); int finalize_job(); int is_there_more_job(); int main(int argc, char* argv[]) { int state = 1; /* state of the "protocol" state machine */ int res; arg_parse(argc, argv); /* Finite state machine */ while ( state > 0 ) { fprintf(stderr, "Now in %s state\n", state_str[state]); switch ( state ) { case 1: state = (wait_hello_and_connect_back() == 0)?2:1; break; case 2: state = (wait_start_and_start_job() == 0)?2:3; break; case 3: res = receive_data(); if (res==0) state = 4; else if (res==1) state=3; else state = -1; break; case 4: state = (finalize_job() == 0)?5:-2; break; case 5: state = (is_there_more_job() == 0)?2:0; break; } } if ( mcast_sock > 0 ) { close(mcast_sock); } if ( state < 0 ) return -state; return EXIT_SUCCESS; } int wait_hello_and_connect_back() { /* Buffers for host and service strings after resolve */ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; /* Server address, filled by system after first recvfrom */ struct sockaddr_storage peer_addr; socklen_t peer_addr_len; /* Various needed variables */ ssize_t nread; int res; /* Setup mcast_sock */ if ( mcast_sock > 0 ) { close(mcast_sock); mcast_sock = 0; } mcast_sock = mcast_recv_socket(mcast_ip, port, MULTICAST_SO_RCVBUF); if(mcast_sock < 0) usage("Could not setup multicast socket. Wrong args given ?"); /* Wait for a single datagram from the server (for sync, no check on contain) */ peer_addr_len = sizeof(struct sockaddr_storage); nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, (struct sockaddr *) &peer_addr, &peer_addr_len); if (nread < 0 ) { perror("recvfrom() failed"); return -1; } /* Get peer informations as strings from peer_addr */ res = getnameinfo((struct sockaddr *) &peer_addr, peer_addr_len, hbuf, NI_MAXHOST, sbuf, NI_MAXSERV, NI_NUMERICSERV); if ( res != 0 ) { fprintf(stderr, "getnameinfo: %s\n", gai_strerror(res)); return -2; } /* Connect back to the server, with reliable unicast */ if ( ucast_sock > 0 ) { close(ucast_sock); } ucast_sock = ucast_client_socket(hbuf,port); if(ucast_sock < 0) { fprintf(stderr, "Could not setup unicast socket or connect to %s:%s\n", hbuf, port); return -3; } return 0; } int wait_start_and_start_job() { ssize_t nread, nwrite; /* Wait for a "start" datagram from the server */ nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, NULL, 0); if (nread < 0 ) { perror("recvfrom() failed"); return -1; } if ( nread >= 5 && strncmp("start", recvbuf, 5) == 0 ) { nwrite = write(ucast_sock, "ready", 5); if ( nwrite < 0 ) { fprintf(stderr, "write() failed\n"); return -2; } if (nwrite != 5) { fprintf(stderr, "write() short\n"); return -3; } return 1; } return 0; } int receive_data() { ssize_t nread; uint32_t seq; uint16_t datalen; /* Wait for a "dataN" datagram from the server */ nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, NULL, 0); if (nread < 0 ) { perror("recvfrom() failed"); return -1; } if ( nread >= 10 && strncmp("data", recvbuf, 4) == 0 ) { seq = ntohl( *( (uint32_t *) recvbuf+1 ) ); datalen = ntohs( *( (uint16_t *) recvbuf+4 ) ); //fprintf(stderr, "debug seq==%i, datalen==%hi\n", seq, datalen); if ( nread != (10 + datalen) ) { fprintf(stderr, "debug nread==%zi, (10 + datalen)==%i\n", nread, (10 + datalen)); //TODO nack ? return -2; } fprintf(stdout, "data #%i, ", seq); fwrite(recvbuf+10, datalen, 1, stdout); fflush(stdout); //TODO buffer zero copy, ack return 1; } return 0; } int finalize_job() { return 0; } int is_there_more_job() { return 1; } void die(char* msg) { fprintf(stderr, "%s\n", msg); if (mcast_sock > 0) close(mcast_sock); if (ucast_sock > 0) close(ucast_sock); exit(EXIT_FAILURE); } void usage(char *msg) { char ubuf[256]; if ( msg != NULL ) fprintf(stderr, "%s\n", msg); ubuf[0] = '\0'; snprintf(ubuf, 255, "Usage: %s [port] [mcast_ip]\n", prog_name); die(ubuf); } void arg_parse(int argc, char* argv[]) { prog_name = argv[0]; if ( argc > 3 ) usage("Too many arguments"); port = (argc >= 2)?argv[1]:DEFAULT_PORT_STR; mcast_ip = (argc >= 3)?argv[2]:DEFAULT_MCAST_IP_STR; }