SimGrid
The classical Ping-Pong in GRAS

This example implements the very classical ping-pong in GRAS. It involves a client (initiating the ping-pong) and a server (answering to client's requests).

It works the following way:

This example resides in the examples/gras/ping/ping.c file. Yes, both the code of the client and of the server is placed in the same file. See the Lesson 1: Setting up your own project of the tutorial if wondering.

Table of contents of the ping example


1) Common code to the client and the server

1.a) Initial settings

Let's first load the module header and declare a logging category (see Logging support for more info on logging).

#include "ping.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(Ping, "Messages specific to this example");

The module header ping.h reads:

#include "gras.h"

/* register messages which may be sent (common to client and server) */
void ping_register_messages(void);

/* Function prototypes */
int server(int argc, char *argv[]);
int client(int argc, char *argv[]);

1.b) Register the messages

This function, called by both the client and the server is in charge of declaring the existing messages to GRAS. Since the payload does not involve any newly created types but only int, this is quite easy. (to exchange more complicated types, see Data description or A simple RPC for matrix multiplication for an example).

void ping_register_messages(void)
{
  gras_msgtype_declare("ping", gras_datadesc_by_name("int"));
  gras_msgtype_declare("pong", gras_datadesc_by_name("int"));
  XBT_INFO("Messages registered");
}

[Back to Table of contents of the ping example]

2) Server's code

2.a) The server's globals

In order to ensure the communication between the "main" and the callback of the server, we need to declare some globals. We have to put them in a struct definition so that they can be handled properly in GRAS (see the Lesson 5: Using globals in processes for more info).

typedef struct {
  gras_socket_t sock;
  int endcondition;
} server_data_t;

2.b) The callback to the ping message

Here is the callback run when the server receives any ping message (this will be registered later by the server).

static int server_cb_ping_handler(gras_msg_cb_ctx_t ctx, void *payload)
{
  /* 1. Get the payload into the msg variable, and retrieve my caller */
  int msg = *(int *) payload;
  gras_socket_t expeditor = gras_msg_cb_ctx_from(ctx);

  /* 2. Retrieve the server's state (globals) */

  server_data_t *globals = (server_data_t *) gras_userdata_get();
  globals->endcondition = 0;

  /* 3. Log which client connected */
  XBT_INFO(">>>>>>>> Got message PING(%d) from %s:%d <<<<<<<<",
        msg,
        gras_socket_peer_name(expeditor),
        gras_socket_peer_port(expeditor));

  /* 4. Change the value of the msg variable */
  msg = 4321;
  /* 5. Send it back as payload of a pong message to the expeditor */
  TRY {
    gras_msg_send(expeditor, "pong", &msg);

    /* 6. Deal with errors: add some details to the exception */
  }
  CATCH_ANONYMOUS {
    gras_socket_close(globals->sock);
    RETHROWF("Unable answer with PONG: %s");
  }

  XBT_INFO(">>>>>>>> Answered with PONG(4321) <<<<<<<<");

  /* 7. Set the endcondition boolean to true (and make sure the server stops after receiving it). */
  globals->endcondition = 1;

  /* 8. Tell GRAS that we consummed this message */
  return 0;
}                               /* end_of_server_cb_ping_handler */

2.c) The "main" of the server

This is the "main" of the server. As explained in the tutorial, Lesson 1: Setting up your own project, you must not write any main() function yourself. Instead, you just have to write a regular function like this one which will act as a main.

int server(int argc, char *argv[])
{
  server_data_t *globals;

  int port = 4000;

  /* 1. Init the GRAS infrastructure and declare my globals */
  gras_init(&argc, argv);
  globals = gras_userdata_new(server_data_t);

  /* 2. Get the port I should listen on from the command line, if specified */
  if (argc == 2) {
    port = atoi(argv[1]);
  }

  XBT_INFO("Launch server (port=%d)", port);

  /* 3. Create my master socket */
  globals->sock = gras_socket_server(port);

  /* 4. Register the known messages. This function is called twice here, but it's because
     this file also acts as regression test, no need to do so yourself of course. */
  ping_register_messages();
  ping_register_messages();     /* just to make sure it works ;) */

  /* 5. Register my callback */
  gras_cb_register("ping", &server_cb_ping_handler);

  XBT_INFO(">>>>>>>> Listening on port %d <<<<<<<<",
        gras_socket_my_port(globals->sock));
  globals->endcondition = 0;

  /* 6. Wait up to 10 minutes for an incomming message to handle */
  gras_msg_handle(10.0);

  /* 7. Housekeeping */
  if (!globals->endcondition)
    XBT_WARN
        ("An error occured, the endcondition was not set by the callback");

  /* 8. Free the allocated resources, and shut GRAS down */
  gras_socket_close(globals->sock);
  free(globals);
  XBT_INFO("Done.");
  gras_exit();

  return 0;
}                               /* end_of_server */

[Back to Table of contents of the ping example]

3) Client's code

3.a) Client's "main" function

This function is quite straightforward, and the inlined comments should be enough to understand it.

int client(int argc, char *argv[])
{
  xbt_ex_t e;
  gras_socket_t toserver = NULL;        /* peer */
  int connected = 0;

  gras_socket_t from;
  int ping, pong;

  const char *host = "127.0.0.1";
  int port = 4000;

  /* 1. Init the GRAS's infrastructure */
  gras_init(&argc, argv);

  /* 2. Get the server's address. The command line override defaults when specified */
  if (argc == 3) {
    host = argv[1];
    port = atoi(argv[2]);
  }

  XBT_INFO("Launch client (server on %s:%d)", host, port);

  /* 3. Create a socket to speak to the server */
  while (!connected) {
    TRY {
      toserver = gras_socket_client(host, port);
      connected = 1;
    }
    CATCH(e) {
      if (e.category != system_error)
        /* dunno what happened, let the exception go through */
        RETHROWF("Unable to connect to the server: %s");
      xbt_ex_free(e);
      gras_os_sleep(0.05);
    }
  }

  XBT_INFO("Connected to %s:%d.", host, port);

  /* 4. Register the messages.
     See, it doesn't have to be done completely at the beginning, only before use */
  ping_register_messages();

  /* 5. Keep the user informed of what's going on */
  XBT_INFO(">>>>>>>> Connected to server which is on %s:%d <<<<<<<<",
        gras_socket_peer_name(toserver), gras_socket_peer_port(toserver));

  /* 6. Prepare and send the ping message to the server */
  ping = 1234;
  TRY {
    gras_msg_send(toserver, "ping", &ping);
  }
  CATCH_ANONYMOUS {
    gras_socket_close(toserver);
    RETHROWF("Failed to send PING to server: %s");
  }
  XBT_INFO(">>>>>>>> Message PING(%d) sent to %s:%d <<<<<<<<",
        ping, gras_socket_peer_name(toserver), gras_socket_peer_port(toserver));

  /* 7. Wait for the answer from the server, and deal with issues */
  TRY {
    gras_msg_wait(6000, "pong", &from, &pong);
  }
  CATCH_ANONYMOUS {
    gras_socket_close(toserver);
    RETHROWF("Why can't I get my PONG message like everyone else: %s");
  }

  /* 8. Keep the user informed of what's going on, again */
  XBT_INFO(">>>>>>>> Got PONG(%d) from %s:%d <<<<<<<<",
        pong, gras_socket_peer_name(from), gras_socket_peer_port(from));

  /* 9. Free the allocated resources, and shut GRAS down */
  gras_socket_close(toserver);
  XBT_INFO("Done.");
  gras_exit();
  return 0;
}                               /* end_of_client */

[Back to Table of contents of the ping example]


Back to the main Simgrid Documentation page The version of Simgrid documented here is v3.6.1.
Documentation of other versions can be found in their respective archive files (directory doc/html).
Generated for SimGridAPI by doxygen