Example 4

[ Back to Software Project Homepage ]


Introduction

This example is intended as a quick starting point for learning the basic concept of multi-threaded programming. A program with multiple threads is required when its task is to send and receive data at the same time, or when it has to listen on several ports at the same time. This example shows a simple ping like application where a client periodically sends small packets to the specified server; and the server upon reception of a packet echoes it back to the client. As this form of communication is asynchronous (i.e., the client needs to send packets at a given rate, without having to wait for the return packet), it needs a dedicated task (a thread) to listen for new incoming packets while the main task is taking care of sending packets.

Source Code can be found in the package.

Code Analysis:

Server class

The server code is very simple: it listens for packets and immediately sends them back when they are received.

Client

The client class is instead the most interesting one as it provides the example of how starting a new thread that will be used to listen for incoming packets. The important parts used to handle these operations are shown in the following table:

ping-client.cpp
ping-server.cpp
 void *clientRecProc(void *arg){       printf("Started receiving thread\n");
      struct cShared *sh = (struct cShared *)arg;
      Packet *recvPacket;
      PacketHdr *hdr;
      int seq_n;
      char type;
      int size;
      for(int i=0; i<sh->max; i++){
            recvPacket = sh->my_recv_port->receivePacket();
            hdr = recvPacket->accessHeader();
            type = hdr->getOctet(0);
            seq_n = hdr->getIntegerInfo(1);
            size = recvPacket->getPayloadSize();
            printf("Received packet: type = %c, sequence number = %d, size = %d\n", type,                   seq_n, size);
      }
      return NULL;
 }

 void startClient(int n){
      ...
      struct cShared *sh;
      sh = (struct cShared*)malloc(sizeof(struct cShared));
      sh->my_recv_port = my_recv_port;
      sh->max = n;
      pthread_t thread;
      printf("Creating receiving thread\n");
      pthread_create(&(thread), 0, &clientRecProc, sh);
      int seq_n;
      char buffer[PING_SIZE];
      memset(buffer, 0, PING_SIZE);
      Packet *my_packet = new Packet();
      my_packet->fillPayload(PING_SIZE, buffer);
      PacketHdr *hdr = my_packet->accessHeader();
      hdr->setOctet('0',0);
      for(seq_n=0; seq_n<n; seq_n++){
            printf("Sending packet %d\n",seq_n);
            hdr->setIntegerInfo(seq_n,1);
            my_send_port->sendPacket(my_packet);
            usleep(1000000);
      }
      pthread_join(thread, NULL);
      delete my_packet;
 }
 printf("Start server\n");
 Packet *recvPacket;
 PacketHdr *hdr;
 int seq_n;
 char type;
 int size;
 while(1){
      recvPacket = my_recv_port->receivePacket();
      hdr = recvPacket->accessHeader();
      type = hdr->getOctet(0);
      seq_n = hdr->getIntegerInfo(1);
      size = recvPacket->getPayloadSize();
      printf("Received packet: type = %c, sequence number = %d, size = %d\n", type,             seq_n, size);
      hdr->setOctet('1', 0);
      my_send_port->sendPacket(recvPacket);
}

Look at the code:

pthread_create(&(thread), 0, &clientRecProc, sh);

This initiate a new thread that will receive sh as a parameter (note that the parameter is received by the function as a void pointer that will need to be properly casted). Once the main thread will finish its work (sending the proper number of messages) it invokes:

pthread_join(thread, NULL);

to properly close the life of the process.

NOTE: this example provides a very quick introduction to threads, there is so much more that would need to be explained! Please, refer to on of the multiple tutorials that are available online (for example this is a valid tutorial).

Compile and Test the program

make
./ping-client 10
./ping-server
Start ping client
Creating receiving thread
Sending packet 0
Started receiving thread
Received packet: type = 1, sequence number = 0, size = 64
Sending packet 1
Received packet: type = 1, sequence number = 1, size = 64
Sending packet 2
Received packet: type = 1, sequence number = 2, size = 64
Sending packet 3
Received packet: type = 1, sequence number = 3, size = 64
Sending packet 4
Received packet: type = 1, sequence number = 4, size = 64
Sending packet 5
Received packet: type = 1, sequence number = 5, size = 64
Sending packet 6
Received packet: type = 1, sequence number = 6, size = 64
Sending packet 7
Received packet: type = 1, sequence number = 7, size = 64
Sending packet 8
Received packet: type = 1, sequence number = 8, size = 64
Sending packet 9
Received packet: type = 1, sequence number = 9, size = 64
Start server
Received packet: type = 0, sequence number = 0, size = 64
Received packet: type = 0, sequence number = 1, size = 64
Received packet: type = 0, sequence number = 2, size = 64
Received packet: type = 0, sequence number = 3, size = 64
Received packet: type = 0, sequence number = 4, size = 64
Received packet: type = 0, sequence number = 5, size = 64
Received packet: type = 0, sequence number = 6, size = 64
Received packet: type = 0, sequence number = 7, size = 64
Received packet: type = 0, sequence number = 8, size = 64
Received packet: type = 0, sequence number = 9, size = 64