The sender would have code like this
integer commChannel = -837465; 
...  
     llRegionSay( commChannel, "start");
...
integer commChannel = -837465; 
default { 
  state_entry() {
      llListen( commChannel, "", NULL_KEY, "");
  }
  listen( integer channel, string fromName, 
key fromKey, string msg ){
key fromKey, string msg ){
    if ( channel == commChannel ) {
      if ( msg == "start" ) { 
        llSay(PUBLIC_CHANNEL, "'"+fromName+"' says start"); 
      } else if ( msg == "stop" ) { 
        llSay(PUBLIC_CHANNEL, "'"+fromName+"' says stop"); 
      }
    }
  }
}
____________________Communication often consists pairs of messages that comprise a transaction. For a ping-pong transaction which allows one object to find many other objects, the receiver would have code like this
  listen( integer channel, string fromName, key fromKey, 
string msg ){
string msg ){
    if ( channel == commChannel ) {
      integer match = llListFindList( 
[ "start", "stop", "ping" ], [msg] );
[ "start", "stop", "ping" ], [msg] );
      if ( match == 0 ) { 
        .... 
      } else if ( match == 1 ) { 
        ....
      } else if ( match == 2 ) {
llRegionSayTo( fromKey, commChannel, "pong" );
llRegionSayTo( fromKey, commChannel, "pong" );
      }
    } 
  }
The receiver knows that the pong should go back to the emitter of the ping. Therefore the pong emitter may use llRegionSayTo() to send the message to a specific object.
The pinger code would include a listen handle that receives the pong message. To make a list of responder keys it could have code like this:
  list pongerKeyList = [];
  listen( integer channel, string fromName, 
key fromKey, string msg ){
key fromKey, string msg ){
    if ( channel == commChannel ) {
      integer match = llListFindList( [ "pong" ], [msg] );
      if ( match == 0 ) { 
         pongerKeyList += fromKey;
      }
    } 
  }
____________________The receiver of a message can extract substantial information from the key of the sender using llGetObjectDetails().
  listen( integer channel, string fromName, 
key fromKey, string msg ){
key fromKey, string msg ){
    if ( channel == commChannel ) {
      if ( msg == "pong" ) { 
        list a = llGetObjectDetails( fromKey, 
[ OBJECT_NAME, OBJECT_OWNER,
OBJECT_POS, OBJECT_ROT ] );
[ OBJECT_NAME, OBJECT_OWNER,
OBJECT_POS, OBJECT_ROT ] );
        llSay(PUBLIC_CHANNEL, "pong response: "+llList2CSV(a));
      } 
    } 
  }
____________________Messages carry additional information by concatenating values using a special character pattern which does not appear within any values. The message sender uses llDumpList2String() to build the message. The receiver used llParseStringKeepNulls() to separate the message into a list of string fields. These string fields are converted back into values using casts. The ping sender would have code like this:
  touch_start( integer nTouch ) {
     float jitterPongMax = 4; 
llRegionSay( commChannel, llDumpList2String(
[ "ping", jitterPongMax , llGetUnixTime(),
llRegionSay( commChannel, llDumpList2String(
[ "ping", jitterPongMax , llGetUnixTime(),
                       llGetPos() ], "|"));
}
}
The ping receiver/pong sender would have code like this:
listen( integer channel, string fromName,
                     key fromKey,   string msg ){
    if ( channel == commChannel ) {
      list a = llParseStringKeepNulls( msg, ["|"], [] ); 
string verb = llList2String(a,0);
integer match = llListFindList(
[ "start", "stop", "ping" ], [verb] );
if ( match == 0 ) {
...
string verb = llList2String(a,0);
integer match = llListFindList(
[ "start", "stop", "ping" ], [verb] );
if ( match == 0 ) {
...
     } else if ( match == 1 ) {       ...
} else if ( match == 2 ) {
float jitterMax = (integer)llList2String(a,0);
integer sendTime = (integer)llList2String(a,1);
vector pingerPos = (vector) llList2String(a,3);
llSleep( llFrand(jitterMax ) );
llRegionSayTo( fromKey, commChannel, "pong" );
.... }
} else if ( match == 2 ) {
float jitterMax = (integer)llList2String(a,0);
integer sendTime = (integer)llList2String(a,1);
vector pingerPos = (vector) llList2String(a,3);
llSleep( llFrand(jitterMax ) );
llRegionSayTo( fromKey, commChannel, "pong" );
.... }
    } 
  }
____________________
Notes:
[end]
Notes:
- If your system will be deployed across region boundaries, then use llShout(), which has a range of 100 meters.
- If the messages pass between one agent's attachments, use llWhisper(), which has a range of 10 meters.
- In the last example llSleep(llFrand(jitterMax)) prevents all the pongers from responding at the same time. There are limits to how many messages can be queued by a region.
- llParseStringKeepNulls() is used because string values may be zero length strings.
- llListFindList() and subsequent integer comparisons is more efficient that a series of string comparisons.
 

