virtual void Pulse(const PulseArgs & args)
   {
      AbstractReflectSession::Pulse(args);

      while(_pendingReplies.HasItems())
      {
         const uint64 nextSendTime = *_pendingReplies.GetFirstValue();
         if (args.GetCallbackTime() >= nextSendTime)
         {
            MessageRef msgToSend;
            if (_pendingReplies.RemoveFirst(msgToSend) == B_NO_ERROR) 
            {
               IPAddressAndPort dest; (void) msgToSend()->FindFlat(PR_NAME_PACKET_REMOTE_LOCATION, dest);

               printf("Sending UDP reply to [%s]:\n", dest.ToString()());
               ByteBufferRef msgData; (void) msgToSend()->FindFlat(PR_NAME_DATA_CHUNKS, msgData);
               PrintHexBytes(msgData);

               // Hand the Message off to the RawMessageDataGateway for immediate transmission
               (void) AddOutgoingMessage(msgToSend);
            }
         }
         else break;  // nothing to do yet!?
      }
   }
   virtual void MessageReceivedFromGateway(const MessageRef & msg, void * userData)
   {
      ByteBufferRef receivedData;
      if (msg()->FindFlat(PR_NAME_DATA_CHUNKS, receivedData) == B_NO_ERROR)
      {
         IPAddressAndPort sourceIAP;
         if (msg()->FindFlat(PR_NAME_PACKET_REMOTE_LOCATION, sourceIAP) == B_NO_ERROR)
         {
            printf("Received from [%s]:\n", sourceIAP.ToString()());
            PrintHexBytes(receivedData);

            // If we wanted to reply immediately, we could just call AddOutgoingMessage(msg) right here
            // But example_2_udp_pingpong waits 100mS before sending back the reply, so let's do that
            // here as well.  We'll use Pulse() and GetPulseTime() to implement without blocking the
            // server's event loop.
            if (_pendingReplies.Put(msg, GetRunTime64()+MillisToMicros(100)) == B_NO_ERROR) InvalidatePulseTime(); 
         }
         else LogTime(MUSCLE_LOG_ERROR, "Error, gateway didn't provide the UDP packet's source location?!\n");
      }
   }
int32 PacketTunnelIOGateway :: DoInputImplementation(AbstractGatewayMessageReceiver & receiver, uint32 maxBytes)
{
   if (_inputPacketBuffer.SetNumBytes(_maxTransferUnit, false) != B_NO_ERROR) return -1;

   bool firstTime = true;
   uint32 totalBytesRead = 0;
   while((totalBytesRead < maxBytes)&&((firstTime)||(IsSuggestedTimeSliceExpired() == false)))
   {
      firstTime = false;

      int32 bytesRead = GetDataIO()()->Read(_inputPacketBuffer.GetBuffer(), _inputPacketBuffer.GetNumBytes());
//printf("   READ " INT32_FORMAT_SPEC "/" UINT32_FORMAT_SPEC " bytes\n", bytesRead, _inputPacketBuffer.GetNumBytes());
      if (bytesRead > 0)
      {
         totalBytesRead += bytesRead;

         IPAddressAndPort fromIAP;
         const PacketDataIO * packetIO = dynamic_cast<PacketDataIO *>(GetDataIO()());
         if (packetIO) fromIAP = packetIO->GetSourceOfLastReadPacket();

         const uint8 * p = (const uint8 *) _inputPacketBuffer.GetBuffer();
         if ((_allowMiscData)&&((bytesRead < (int32)FRAGMENT_HEADER_SIZE)||(((uint32)B_LENDIAN_TO_HOST_INT32(muscleCopyIn<uint32>(p))) != _magic)))
         {
            // If we're allowed to handle miscellaneous data, we'll just pass it on through verbatim
            ByteBuffer temp;
            temp.AdoptBuffer(bytesRead, const_cast<uint8 *>(p));
            HandleIncomingMessage(receiver, ByteBufferRef(&temp, false), fromIAP);
            (void) temp.ReleaseBuffer();
         }
         else
         {
            const uint8 * invalidByte = p+bytesRead;
            while(invalidByte-p >= (int32)FRAGMENT_HEADER_SIZE)
            {
               const uint32 magic     = B_LENDIAN_TO_HOST_INT32(muscleCopyIn<uint32>(&p[0*sizeof(uint32)]));
               const uint32 sexID     = B_LENDIAN_TO_HOST_INT32(muscleCopyIn<uint32>(&p[1*sizeof(uint32)]));
               const uint32 messageID = B_LENDIAN_TO_HOST_INT32(muscleCopyIn<uint32>(&p[2*sizeof(uint32)]));
               const uint32 offset    = B_LENDIAN_TO_HOST_INT32(muscleCopyIn<uint32>(&p[3*sizeof(uint32)]));
               const uint32 chunkSize = B_LENDIAN_TO_HOST_INT32(muscleCopyIn<uint32>(&p[4*sizeof(uint32)]));
               const uint32 totalSize = B_LENDIAN_TO_HOST_INT32(muscleCopyIn<uint32>(&p[5*sizeof(uint32)]));
//printf("   PARSE magic=" UINT32_FORMAT_SPEC "/" UINT32_FORMAT_SPEC " sex=" UINT32_FORMAT_SPEC "/" UINT32_FORMAT_SPEC " messageID=" UINT32_FORMAT_SPEC " offset=" UINT32_FORMAT_SPEC " chunkSize=" UINT32_FORMAT_SPEC " totalSize=" UINT32_FORMAT_SPEC "\n", magic, _magic, sexID, _sexID, messageID, offset, chunkSize, totalSize);

               p += FRAGMENT_HEADER_SIZE;
               if ((magic == _magic)&&((_sexID == 0)||(_sexID != sexID))&&((invalidByte-p >= (int32)chunkSize)&&(totalSize <= _maxIncomingMessageSize)))
               {
                  ReceiveState * rs = _receiveStates.Get(fromIAP);
                  if (rs == NULL)
                  {
                     if (offset == 0) rs = _receiveStates.PutAndGet(fromIAP, ReceiveState(messageID));
                     if (rs)
                     {
                        rs->_buf = GetByteBufferFromPool(totalSize);
                        if (rs->_buf() == NULL)
                        {
                           _receiveStates.Remove(fromIAP);
                           rs = NULL;
                        }
                     }
                  }
                  if (rs)
                  {
                     if ((offset == 0)||(messageID != rs->_messageID))
                     {
                        // A new message... start receiving it (but only if we are starting at the beginning)
                        rs->_messageID = messageID;
                        rs->_offset    = 0;
                        rs->_buf()->SetNumBytes(totalSize, false);
                     }

                     uint32 rsSize = rs->_buf()->GetNumBytes();
//printf("  CHECK:  offset=" UINT32_FORMAT_SPEC "/" UINT32_FORMAT_SPEC " %s\n", offset, rs->_offset, (offset==rs->_offset)?"":"DISCONTINUITY!!!");
                     if ((messageID == rs->_messageID)&&(totalSize == rsSize)&&(offset == rs->_offset)&&(offset+chunkSize <= rsSize))
                     {
                        memcpy(rs->_buf()->GetBuffer()+offset, p, chunkSize);
                        rs->_offset += chunkSize;
                        if (rs->_offset == rsSize) 
                        {
                           HandleIncomingMessage(receiver, rs->_buf, fromIAP);
                           rs->_offset = 0;
                           rs->_buf()->Clear(rsSize > MAX_CACHE_SIZE);
                        }
                     }
                     else 
                     {
                        LogTime(MUSCLE_LOG_DEBUG, "Unknown fragment (" UINT32_FORMAT_SPEC "/" UINT32_FORMAT_SPEC "/" UINT32_FORMAT_SPEC "/" UINT32_FORMAT_SPEC ") received from %s, ignoring it.\n", messageID, offset, chunkSize, totalSize, fromIAP.ToString()());
                        rs->_offset = 0;
                        rs->_buf()->Clear(rsSize > MAX_CACHE_SIZE);
                     }
                  }
                  p += chunkSize;
               }
               else break;
            }
         }
      }
      else if (bytesRead < 0) return -1;
      else break;
   }
   return totalBytesRead;
}
int main(int argc, char ** argv)
{
   CompleteSetupSystem css;

   PrintExampleDescription();

   // Let's enable a bit of debug-output, just to see what the client is doing
   SetConsoleLogLevel(MUSCLE_LOG_DEBUG);

   MessageTransceiverThread mtt;
   if (mtt.StartInternalThread() != B_NO_ERROR)
   {
      LogTime(MUSCLE_LOG_CRITICALERROR, "Couldn't start the MessageTransceiverThread, aborting!\n");
      return 10;
   }

   if (mtt.AddNewConnectSession(localhostIP, SMART_SERVER_TCP_PORT, SecondsToMicros(1)) != B_NO_ERROR)
   {
      LogTime(MUSCLE_LOG_CRITICALERROR, "mtt.AddNewConnectSession() failed, aborting!\n");
      mtt.ShutdownInternalThread();
      return 10;
   }

   LogTime(MUSCLE_LOG_INFO, "This program is designed to be run in conjunction with example_4_smart_server\n");
   LogTime(MUSCLE_LOG_INFO, "You'll probably want to run multiple instances of this client at the same time, also.\n");
   printf("\n");
   PrintHelp();

   printf("\n");
   LogTime(MUSCLE_LOG_INFO, "Some example commands that you can enter:\n");
   LogTime(MUSCLE_LOG_INFO, "   subscribe /*/*       -> will set up a subscription that always lets you know who is connected\n");
   LogTime(MUSCLE_LOG_INFO, "   subscribe /*/*/*     -> will set up a subscription that always lets you know who set/deleted/updated a node\n");
   LogTime(MUSCLE_LOG_INFO, "   subscribe *          -> is the same as the previous command (the initial wildcards can be implicit)\n");
   LogTime(MUSCLE_LOG_INFO, "   set frood = groovy   -> create a node named 'frood' in your session-folder, with the word 'groovy' in its Message\n");
   LogTime(MUSCLE_LOG_INFO, "   delete frood         -> delete the node named 'frood' in your session-folder\n");
   LogTime(MUSCLE_LOG_INFO, "   delete f*            -> delete all nodes in your session-folder whose names start with f\n");
   LogTime(MUSCLE_LOG_INFO, "   delete *             -> delete all nodes in your session-folder\n");
   LogTime(MUSCLE_LOG_INFO, "   msg /*/* hello       -> say hello to everyone who is connected\n");
   LogTime(MUSCLE_LOG_INFO, "   msg /*/*/frood hello -> say hello to everyone who is connected and created a node named \'frood\' in their session-folder\n");
   LogTime(MUSCLE_LOG_INFO, "   die                  -> cause the client process to exit\n");
   printf("\n");

   // Run our own event loop to read from stdin and retrieve
   // feedback events from the MessageTransceiverThread.
   // (In other contexts this might be a QEventLoop or
   // a Win32 event loop or an SDL event loop or etc; anything
   // where the main thread needs to be doing some non-MUSCLE
   // event loop is a good use case for QMessageTransceiverThread)

   StdinDataIO stdinIO(false);
   SocketMultiplexer sm;
   while(true)
   {
      sm.RegisterSocketForReadReady(stdinIO.GetReadSelectSocket().GetFileDescriptor());
      sm.RegisterSocketForReadReady(mtt.GetOwnerWakeupSocket().GetFileDescriptor());

      sm.WaitForEvents();

      if (sm.IsSocketReadyForRead(stdinIO.GetReadSelectSocket().GetFileDescriptor()))
      {
         // Handle stdin input, and send a Message to the MessageTransceiverThread
         // for it to send on to the server, if appropriate
         uint8 inputBuf[1024];
         const int numBytesRead = stdinIO.Read(inputBuf, sizeof(inputBuf)-1);
         if (numBytesRead > 0)
         {
            String inputCmd((const char *) inputBuf, numBytesRead);
            inputCmd = inputCmd.Trim();
            if (inputCmd == "die") break;

            MessageRef msgToSend = ParseStdinCommand(inputCmd);
            if (msgToSend())
            {
               printf("Calling mtt.SendMessageToSessions() with the following Message:\n");
               msgToSend()->PrintToStream();
              (void) mtt.SendMessageToSessions(msgToSend);
            }
         }
         else if (numBytesRead < 0) break;
      }

      if (sm.IsSocketReadyForRead(mtt.GetOwnerWakeupSocket().GetFileDescriptor()))
      {
         // Handle any feedback events sent back to us from the MessageTransceiverThread
         uint32 code;
         MessageRef ref;
         String session;
         uint32 factoryID;
         IPAddressAndPort location;
         while(mtt.GetNextEventFromInternalThread(code, &ref, &session, &factoryID, &location) >= 0)
         {
            String codeStr;
            switch(code)
            {
               case MTT_EVENT_INCOMING_MESSAGE:      codeStr = "IncomingMessage";     break;
               case MTT_EVENT_SESSION_ACCEPTED:      codeStr = "SessionAccepted";     break;
               case MTT_EVENT_SESSION_ATTACHED:      codeStr = "SessionAttached";     break;
               case MTT_EVENT_SESSION_CONNECTED:     codeStr = "SessionConnected";    break;
               case MTT_EVENT_SESSION_DISCONNECTED:  codeStr = "SessionDisconnected"; break;
               case MTT_EVENT_SESSION_DETACHED:      codeStr = "SessionDetached";     break;
               case MTT_EVENT_FACTORY_ATTACHED:      codeStr = "FactoryAttached";     break;
               case MTT_EVENT_FACTORY_DETACHED:      codeStr = "FactoryDetached";     break;
               case MTT_EVENT_OUTPUT_QUEUES_DRAINED: codeStr = "OutputQueuesDrained"; break;
               case MTT_EVENT_SERVER_EXITED:         codeStr = "ServerExited";        break;
               default:                              codeStr = String("\'%1\'").Arg(GetTypeCodeString(code)); break;
            }
            printf("Event from MTT:  type=[%s], session=[%s] factoryID=[" UINT32_FORMAT_SPEC "] location=[%s]\n", codeStr(), session(), factoryID, location.ToString()());
            if (ref()) ref()->PrintToStream();
         }
      }
   }

   mtt.ShutdownInternalThread();

   return 0;
}
 virtual ThreadWorkerSessionRef CreateThreadWorkerSession(const String & loc, const IPAddressAndPort & iap)
 {
    AdvancedThreadWorkerSession * ret = newnothrow AdvancedThreadWorkerSession();
    if (ret) printf("AdvancedThreadWorkerSessionFactory created AdvancedThreadWorkerSession %p for client at loc=[%s] iap=[%s]\n", ret, loc(), iap.ToString()());
        else WARN_OUT_OF_MEMORY;
    return ThreadWorkerSessionRef(ret);
 }
 virtual AbstractReflectSessionRef CreateSession(const String & clientAddress, const IPAddressAndPort & factoryInfo)
 {
    printf("MyDumbReflectSessionFactory::CreateSession() called!  (clientAddress=[%s] factoryInfo=[%s])\n", clientAddress(), factoryInfo.ToString()());
    return AbstractReflectSessionRef(new MyDumbReflectSession);
 }