/****************************************************************************** * FILE: UDPThreads.c * DESCRIPTION: * The main thread creates two auxiliary threads. The first creates a * UDP socket and sends messages to the local host. The second thread * monitors the port these messages are sent to and counts the number * of received messages. Once a fixed number of messages is * received. the main thread is notified and the program shuts down * gracefully. * * Compile with: gcc -pthread UDPThreads.c -o UDPThreads * * SOURCE: based on code examples from * http://www.llnl.gov/computing/tutorials/pthreads/ and the TCP/IP * sockets in C book. * LAST REVISED: 3/13/05 pparis@gmu.edu ******************************************************************************/ #include /* for pthread_mutex_t, pthread_cond_t, pthread_t, pthread_attr_t, pthread_mutex_init(), pthread_cond_init(), pthread_attr_init(), pthread_attr_setdetachstate(), pthread_create(), pthread_exit(), pthread_join(), pthread_attr_destroy(), pthread_mutex_destroy(), pthread_cond_destroy(), pthread_exit(), pthread_mutex_lock(), pthread_mutex_unlock(), pthread_cond_wait(), pthread_cond_signal() */ #include /* for printf() */ #include /* for socket(), connect(), sendto(), and recvfrom() */ #include /* for sockaddr_in and inet_addr() */ #include /* for atoi() and exit() */ #include /* for memset() */ #include /* for close() */ #define COUNT_LIMIT 3 /* receive this many messages */ #define UDP_PORT 12345 /* UDP port to use */ #define IP_ADDRESS "127.0.0.1" /* IP address of receiver */ #define MESSAGE "Testing, one, two, three ..." /* the message */ #define SLEEP_INTERVAL 3 /* sleep this long between sendto's */ #define ECHOMAX 255 /* maximum receive buffer size */ /* * The following global variables are used for communicating between * threads: * - count: is incremented by the receiving thread; once count * reaches COUNT_LIMIT main thread is notified via condition * variable count_threshold_cv. * - count_threshold_cv: above mentioned condition variable for * notifying main thread. * - count_mutex: mutual exclusion lock (mutex) for * count_threshold_cv. * - timeToQuit: set by main thread to signal other threads to * quit. Threads check this variable periodically, hence, no need * to signal via condition variables. Also, only main thread * writes to this variable, hence no need for a mutex. */ int count = 0; pthread_mutex_t count_mutex; pthread_cond_t count_threshold_cv; int timeToQuit = 0; int thread_ids[2] = {0,1}; /* * subroutine for handling errors. */ void DieWithError(char *errorMessage) { perror(errorMessage); exit(1); } /* * Subroutine executed by the first of the spawned threads: open a UDP * socket with destination IP address IP_ADDRESS and port * UDP_PORT. Periodically transmit a message on that port. Repeat * until signaled to quit via global variable timeToQuit */ void *UDPSender(void *idp) { int *my_id = idp; /* thread id, passed from main */ int SenderSocket; /* socket file descriptor */ struct sockaddr_in ReceiverAddr; /* destination address */ char* Message = MESSAGE; /* buffer containing message */ int MsgLen = strlen(Message); /* length of message string */ char* ReceiverIP = IP_ADDRESS; /* buffer for destination IP address */ printf("\t-->UDPSender: thread %d, Starting...\n", *my_id); /* Create socket */ if ((SenderSocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) DieWithError("socket() failed"); printf("\t-->UDPSender: Socket created.\n"); /* Construct the server address structure */ memset(&ReceiverAddr, 0, sizeof(ReceiverAddr)); /* Zero out structure */ ReceiverAddr.sin_family = AF_INET; /* Internet addr family */ ReceiverAddr.sin_addr.s_addr=inet_addr(ReceiverIP);/* Receiver IP address */ ReceiverAddr.sin_port = htons(UDP_PORT); /* Receiver port */ /* periodically send message until signaled to stop */ while (!timeToQuit) { /* Send message using sendto */ if (sendto(SenderSocket, Message, MsgLen, 0, (struct sockaddr *) &ReceiverAddr, sizeof(ReceiverAddr)) != MsgLen) DieWithError("sendto() sent a different number of bytes than expected"); printf("\t-->UDPSender: Sent message ...\n"); /* sleep until next transmission */ sleep(SLEEP_INTERVAL); } /* Send one more message using sendto just in case the receiver is still waiting for messages (i.e., sits in recvfrom). Is there a more elegant way to prevent deadlock? */ if (sendto(SenderSocket, Message, MsgLen, 0, (struct sockaddr *) &ReceiverAddr, sizeof(ReceiverAddr)) != MsgLen) DieWithError("sendto() sent a different number of bytes than expected"); printf("\t-->UDPSender: Sent message ...\n"); /* close socket */ close(SenderSocket); printf("\t-->UDPSender: closed socket.\n"); /* done, exit thread so that it can be joined by main */ printf("\t-->UDPSender: thread %d, Exiting.\n", *my_id); pthread_exit(NULL); } /* * Subroutine executed by the second thread. Create a socket for * receiving on port UDP_PORT. Each time a message has been received, * increment the global counter (count). Once count reaches * COUNT_LIMIT, signal the main thread. Quit when signaled by main * thread. */ void *UDPReceiver(void *idp) { int *my_id = idp; /* thread id, passed by main */ int ReceiverSocket; /* socket file descriptor */ struct sockaddr_in ReceiverAddr; /* address of receiver */ struct sockaddr_in SenderAddr; /* address of sender, set in recvfrom */ unsigned int SenderAddrLen; /* length of sender address */ char Buffer[ECHOMAX]; /* buffer for received message */ int recvMsgSize; /* length of received message */ printf("\t<--UDPReceiver: thread %d, Starting...\n", *my_id); /* Create socket */ if ((ReceiverSocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) DieWithError("socket() failed"); printf("\t<--UDPReceiver: Socket created.\n"); /* Construct local address structure */ memset(&ReceiverAddr, 0, sizeof(ReceiverAddr)); /* Zero out structure */ ReceiverAddr.sin_family = AF_INET; /* Internet address family */ ReceiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);/* Any incoming interface */ ReceiverAddr.sin_port = htons(UDP_PORT); /* Local port */ /* Bind to the local address */ if (bind(ReceiverSocket, (struct sockaddr *) &ReceiverAddr, sizeof(ReceiverAddr)) < 0) DieWithError("bind() failed"); printf("\t<--UDPReceiver: Socket bound.\n"); /* listen for messages until signaled to stop */ while (!timeToQuit) { SenderAddrLen = sizeof(SenderAddr); /* use recvfrom to wait for incoming messages */ if((recvMsgSize = recvfrom(ReceiverSocket, Buffer, ECHOMAX, 0, (struct sockaddr *)&SenderAddr, &SenderAddrLen)) < 0) DieWithError("recvfrom() failed"); printf("\t<--UDPReceiver: Received: %s.\n", Buffer); /* message received: increment count under mutex protection (not strictly necessary to use mutex since no other thread writes to count). However, the lock must be acquired anyways before the condition variable can be set (see below). */ pthread_mutex_lock(&count_mutex); count++; /* Check the value of count and signal waiting thread when condition is reached. Note that this occurs while mutex is locked. */ if (count == COUNT_LIMIT) { pthread_cond_signal(&count_threshold_cv); printf("\t<--UDPReceiver: thread %d, count = %d Threshold reached.\n", *my_id, count); } /* Release mutex */ printf("\t<--UDPReceiver: thread %d, count = %d.\n\n", *my_id, count); pthread_mutex_unlock(&count_mutex); } /* close socket */ close(ReceiverSocket); printf("\t<--UDPReceiver: closed socket.\n"); /* done, exit thread so that it can be joined by main */ printf("\t<--UDPReceiver: thread %d, Exiting.\n", *my_id); pthread_exit(NULL); } /* * Main thread: create and initialize the two other threads as well as * mutex and condition variable. Then, wait on condition variable * count_threshold_cv. Once that signal has been received, signal * threads to shut down and join exiting threads before final * clean-up. */ int main(int argc, char *argv[]) { int i, num_threads; /* for looping over all threads */ pthread_t threads[2]; /* threads to be created */ pthread_attr_t attr; /* thread attributes */ printf ("Main: Starting...\n"); /* Initialize mutex and condition variable objects */ pthread_mutex_init(&count_mutex, NULL); pthread_cond_init (&count_threshold_cv, NULL); /* For portability, explicitly create threads in a joinable state */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); /* Create the two worker threads */ printf ("Main: Creating thread for UDPSender, id 0.\n"); pthread_create(&threads[0], &attr, UDPSender, (void *)&thread_ids[0]); printf ("Main: Creating thread for UDPReceiver, id 1.\n"); pthread_create(&threads[1], &attr, UDPReceiver, (void *)&thread_ids[1]); num_threads = 2; /* record number of threads created */ /* Lock mutex and wait for signal. Note that the pthread_cond_wait routine will automatically and atomically unlock mutex while it waits. Also, note that if COUNT_LIMIT is reached before we get here, the loop will be skipped to prevent pthread_cond_wait from never returning - this shouldn't happen, however. */ pthread_mutex_lock(&count_mutex); if (count < COUNT_LIMIT) { printf("Main: Waiting for condition signal...\n"); pthread_cond_wait(&count_threshold_cv, &count_mutex); printf("Main: Condition signal received.\n"); } pthread_mutex_unlock(&count_mutex); /* signal other threads to quit by setting global variable timeToQuit */ timeToQuit = 1; printf("Main: Signaled other threads to exit.\n"); /* Wait for all threads to complete */ for (i = 0; i < num_threads; i++) { pthread_join(threads[i], NULL); printf ("Main: Waited on thread %d.\n", i); } /* Clean up and exit */ pthread_attr_destroy(&attr); pthread_mutex_destroy(&count_mutex); pthread_cond_destroy(&count_threshold_cv); printf ("Main: Cleaned up. Exiting...\n"); pthread_exit (NULL); }