static status_t ReadIncomingData(const String & desc, DataIO & readIO, const SocketMultiplexer & multiplexer, Queue<ByteBufferRef> & outQ) { if (multiplexer.IsSocketReadyForRead(readIO.GetReadSelectSocket().GetFileDescriptor())) { uint8 buf[4096]; int32 ret = readIO.Read(buf, sizeof(buf)); if (ret > 0) { LogTime(MUSCLE_LOG_TRACE, "Read " INT32_FORMAT_SPEC " bytes from %s:\n", ret, desc()); LogHexBytes(MUSCLE_LOG_TRACE, buf, ret); ByteBufferRef toNetworkBuf = GetByteBufferFromPool(ret, buf); if (toNetworkBuf()) (void) outQ.AddTail(toNetworkBuf); } else if (ret < 0) {LogTime(MUSCLE_LOG_ERROR, "Error, readIO.Read() returned %i\n", ret); return B_ERROR;} } return B_NO_ERROR; }
// This program is equivalent to the portableplaintext client, except // that we communicate with a child process instead of a socket. int main(int argc, char ** argv) { CompleteSetupSystem css; if (argc < 3) PrintUsageAndExit(); const uint32 numProcesses = atol(argv[1]); if (numProcesses == 0) PrintUsageAndExit(); const char * cmd = argv[2]; Hashtable<String,String> testEnvVars; (void) testEnvVars.Put("Peanut Butter", "Jelly"); (void) testEnvVars.Put("Jelly", "Peanut Butter"); (void) testEnvVars.Put("Oranges", "Grapes"); Queue<DataIORef> refs; for (uint32 i=0; i<numProcesses; i++) { ChildProcessDataIO * dio = new ChildProcessDataIO(false); refs.AddTail(DataIORef(dio)); printf("About To Launch child process #" UINT32_FORMAT_SPEC ": [%s]\n", i+1, cmd); fflush(stdout); ConstSocketRef s = (dio->LaunchChildProcess(argc-2, ((const char **) argv)+2, ChildProcessLaunchFlags(MUSCLE_DEFAULT_CHILD_PROCESS_LAUNCH_FLAGS), NULL, &testEnvVars) == B_NO_ERROR) ? dio->GetReadSelectSocket() : ConstSocketRef(); printf("Finished Launching child process #" UINT32_FORMAT_SPEC ": [%s]\n", i+1, cmd); fflush(stdout); if (s() == NULL) { LogTime(MUSCLE_LOG_CRITICALERROR, "Error launching child process #" UINT32_FORMAT_SPEC " [%s]!\n", i+1, cmd); return 10; } } StdinDataIO stdinIO(false); PlainTextMessageIOGateway stdinGateway; QueueGatewayMessageReceiver stdinInputQueue; stdinGateway.SetDataIO(DataIORef(&stdinIO, false)); SocketMultiplexer multiplexer; for (uint32 i=0; i<refs.GetNumItems(); i++) { printf("------------ CHILD PROCESS #" UINT32_FORMAT_SPEC " ------------------\n", i+1); PlainTextMessageIOGateway ioGateway; ioGateway.SetDataIO(refs[i]); ConstSocketRef readSock = refs[i]()->GetReadSelectSocket(); QueueGatewayMessageReceiver ioInputQueue; while(1) { int readFD = readSock.GetFileDescriptor(); multiplexer.RegisterSocketForReadReady(readFD); const int writeFD = ioGateway.HasBytesToOutput() ? refs[i]()->GetWriteSelectSocket().GetFileDescriptor() : -1; if (writeFD >= 0) multiplexer.RegisterSocketForWriteReady(writeFD); const int stdinFD = stdinIO.GetReadSelectSocket().GetFileDescriptor(); multiplexer.RegisterSocketForReadReady(stdinFD); if (multiplexer.WaitForEvents() < 0) printf("testchildprocess: WaitForEvents() failed!\n"); // First, deliver any lines of text from stdin to the child process if ((multiplexer.IsSocketReadyForRead(stdinFD))&&(stdinGateway.DoInput(ioGateway) < 0)) { printf("Error reading from stdin, aborting!\n"); break; } const bool reading = multiplexer.IsSocketReadyForRead(readFD); const bool writing = ((writeFD >= 0)&&(multiplexer.IsSocketReadyForWrite(writeFD))); const bool writeError = ((writing)&&(ioGateway.DoOutput() < 0)); const bool readError = ((reading)&&(ioGateway.DoInput(ioInputQueue) < 0)); if ((readError)||(writeError)) { printf("Connection closed, exiting.\n"); break; } MessageRef incoming; while(ioInputQueue.RemoveHead(incoming) == B_NO_ERROR) { printf("Heard message from server:-----------------------------------\n"); const char * inStr; for (int i=0; (incoming()->FindString(PR_NAME_TEXT_LINE, i, &inStr) == B_NO_ERROR); i++) printf("Line %i: [%s]\n", i, inStr); printf("-------------------------------------------------------------\n"); } if ((reading == false)&&(writing == false)) break; multiplexer.RegisterSocketForReadReady(readFD); if (ioGateway.HasBytesToOutput()) multiplexer.RegisterSocketForWriteReady(writeFD); } if (ioGateway.HasBytesToOutput()) { printf("Waiting for all pending messages to be sent...\n"); while((ioGateway.HasBytesToOutput())&&(ioGateway.DoOutput() >= 0)) {printf ("."); fflush(stdout);} } } printf("\n\nBye!\n"); return 0; }
// This is a text based test client for the muscled server. It is useful for testing // the server, and could possibly be useful for other things, I don't know. int main(int argc, char ** argv) { CompleteSetupSystem css; String hostName; uint16 port = 2960; if (argc > 1) ParseConnectArg(argv[1], hostName, port, false); ConstSocketRef sock = Connect(hostName(), port, "portablereflectclient", false); if (sock() == NULL) return 10; // We'll receive plain text over stdin StdinDataIO stdinIO(false); PlainTextMessageIOGateway stdinGateway; stdinGateway.SetDataIO(DataIORef(&stdinIO, false)); // And send and receive flattened Message objects over our TCP socket TCPSocketDataIO tcpIO(sock, false); MessageIOGateway tcpGateway; tcpGateway.SetDataIO(DataIORef(&tcpIO, false)); DataIORef networkIORef(&tcpIO, false); AbstractMessageIOGatewayRef gatewayRef(&tcpGateway, false); #ifdef MUSCLE_ENABLE_SSL for (int i=1; i<argc; i++) { const char * a = argv[i]; if (strncmp(a, "publickey=", 10) == 0) { a += 10; // skip past the 'publickey=' part SSLSocketDataIO * sslIO = new SSLSocketDataIO(sock, false, false); DataIORef sslIORef(sslIO); if (sslIO->SetPublicKeyCertificate(a) == B_NO_ERROR) { LogTime(MUSCLE_LOG_INFO, "Using public key certificate file [%s] to connect to server\n", a); networkIORef = sslIORef; gatewayRef.SetRef(new SSLSocketAdapterGateway(gatewayRef)); gatewayRef()->SetDataIO(networkIORef); } else { LogTime(MUSCLE_LOG_CRITICALERROR, "Couldn't load public key certificate file [%s] (file not found?)\n", a); return 10; } } } #endif SocketMultiplexer multiplexer; QueueGatewayMessageReceiver stdinInQueue, tcpInQueue; bool keepGoing = true; uint64 nextTimeoutTime = MUSCLE_TIME_NEVER; while(keepGoing) { const int stdinFD = stdinIO.GetReadSelectSocket().GetFileDescriptor(); const int socketReadFD = networkIORef()->GetReadSelectSocket().GetFileDescriptor(); const int socketWriteFD = networkIORef()->GetWriteSelectSocket().GetFileDescriptor(); multiplexer.RegisterSocketForReadReady(stdinFD); multiplexer.RegisterSocketForReadReady(socketReadFD); if (gatewayRef()->HasBytesToOutput()) multiplexer.RegisterSocketForWriteReady(socketWriteFD); if (multiplexer.WaitForEvents(nextTimeoutTime) < 0) printf("portablereflectclient: WaitForEvents() failed!\n"); const uint64 now = GetRunTime64(); if (now >= nextTimeoutTime) { // For OpenSSL testing: Generate some traffic to the server every 50mS printf("Uploading timed OpenSSL-tester update at time " UINT64_FORMAT_SPEC "\n", now); MessageRef stateMsg = GetMessageFromPool(); stateMsg()->AddString("username", "portablereflectclient"); stateMsg()->AddPoint("position", Point((rand()%100)/100.0f, (rand()%100)/100.0f)); stateMsg()->AddInt32("color", -1); MessageRef uploadMsg = GetMessageFromPool(PR_COMMAND_SETDATA); uploadMsg()->AddMessage("qt_example/state", stateMsg); gatewayRef()->AddOutgoingMessage(uploadMsg); nextTimeoutTime = now + MillisToMicros(50); } // Receive data from stdin if (multiplexer.IsSocketReadyForRead(stdinFD)) { while(1) { const int32 bytesRead = stdinGateway.DoInput(stdinInQueue); if (bytesRead < 0) { printf("Stdin closed, exiting!\n"); keepGoing = false; break; } else if (bytesRead == 0) break; // no more to read } } // Handle any input lines that were received from stdin MessageRef msgFromStdin; while(stdinInQueue.RemoveHead(msgFromStdin) == B_NO_ERROR) { const String * st; for (int32 i=0; msgFromStdin()->FindString(PR_NAME_TEXT_LINE, i, &st) == B_NO_ERROR; i++) { const char * text = st->Cstr(); printf("You typed: [%s]\n", text); bool send = true; MessageRef ref = GetMessageFromPool(); const char * arg1 = (st->Length()>2) ? &text[2] : NULL; switch(text[0]) { case 'm': ref()->what = MAKETYPE("umsg"); if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); ref()->AddString("info", "This is a user message"); break; case 'i': ref()->what = PR_COMMAND_PING; ref()->AddString("Test ping", "yeah"); break; case 's': { ref()->what = PR_COMMAND_SETDATA; MessageRef uploadMsg = GetMessageFromPool(MAKETYPE("HELO")); uploadMsg()->AddString("This node was posted at: ", GetHumanReadableTimeString(GetRunTime64())); if (arg1) ref()->AddMessage(arg1, uploadMsg); } break; case 'k': ref()->what = PR_COMMAND_KICK; if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); break; case 'b': ref()->what = PR_COMMAND_ADDBANS; if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); break; case 'B': ref()->what = PR_COMMAND_REMOVEBANS; if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); break; case 'g': ref()->what = PR_COMMAND_GETDATA; if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); break; case 'G': ref()->what = PR_COMMAND_GETDATATREES; if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); ref()->AddString(PR_NAME_TREE_REQUEST_ID, "Tree ID!"); break; case 'q': keepGoing = send = false; break; case 'p': ref()->what = PR_COMMAND_SETPARAMETERS; if (arg1) ref()->AddString(arg1, ""); break; case 'P': ref()->what = PR_COMMAND_GETPARAMETERS; break; case 'L': { // simulate the behavior of qt_example, for testing OpenSSL problem ref()->what = PR_COMMAND_SETPARAMETERS; ref()->AddBool("SUBSCRIBE:qt_example/state", true); printf("Starting OpenSSL problem test...\n"); nextTimeoutTime = 0; } break; case 'x': { ref()->what = PR_COMMAND_SETPARAMETERS; StringQueryFilter sqf("sc_tstr", StringQueryFilter::OP_SIMPLE_WILDCARD_MATCH, "*Output*"); ref()->AddArchiveMessage("SUBSCRIBE:/*/*/csproj/default/subcues/*", sqf); } break; case 'd': ref()->what = PR_COMMAND_REMOVEDATA; if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); break; case 'D': ref()->what = PR_COMMAND_REMOVEPARAMETERS; if (arg1) ref()->AddString(PR_NAME_KEYS, arg1); break; case 't': { // test all data types ref()->what = 1234; ref()->AddString("String", "this is a string"); ref()->AddInt8("Int8", 123); ref()->AddInt8("-Int8", -123); ref()->AddInt16("Int16", 1234); ref()->AddInt16("-Int16", -1234); ref()->AddInt32("Int32", 12345); ref()->AddInt32("-Int32", -12345); ref()->AddInt64("Int64", 123456789); ref()->AddInt64("-Int64", -123456789); ref()->AddBool("Bool", true); ref()->AddBool("-Bool", false); ref()->AddFloat("Float", 1234.56789f); ref()->AddFloat("-Float", -1234.56789f); ref()->AddDouble("Double", 1234.56789); ref()->AddDouble("-Double", -1234.56789); ref()->AddPointer("Pointer", ref()); ref()->AddFlat("Flat", *ref()); char data[] = "This is some data"; ref()->AddData("Flat", B_RAW_TYPE, data, sizeof(data)); } break; default: printf("Sorry, wot?\n"); send = false; break; } if (send) { printf("Sending message...\n"); ref()->PrintToStream(); gatewayRef()->AddOutgoingMessage(ref); } } } // Handle input and output on the TCP socket const bool reading = multiplexer.IsSocketReadyForRead(socketReadFD); const bool writing = multiplexer.IsSocketReadyForWrite(socketWriteFD); const bool writeError = ((writing)&&(gatewayRef()->DoOutput() < 0)); const bool readError = ((reading)&&(gatewayRef()->DoInput(tcpInQueue) < 0)); if ((readError)||(writeError)) { printf("Connection closed (%s), exiting.\n", writeError?"Write Error":"Read Error"); keepGoing = false; } MessageRef msgFromTCP; while(tcpInQueue.RemoveHead(msgFromTCP) == B_NO_ERROR) { printf("Heard message from server:-----------------------------------\n"); msgFromTCP()->PrintToStream(); printf("-------------------------------------------------------------\n"); } } if (gatewayRef()->HasBytesToOutput()) { printf("Waiting for all pending messages to be sent...\n"); while((gatewayRef()->HasBytesToOutput())&&(gatewayRef()->DoOutput() >= 0)) {printf ("."); fflush(stdout);} } printf("\n\nBye!\n"); return 0; }
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; }
void HandleSession(const ConstSocketRef & sock, bool myTurnToThrow, bool doFlush) { LogTime(MUSCLE_LOG_ERROR, "Beginning catch session (%s)\n", doFlush?"flush enabled":"flush disabled"); TCPSocketDataIO sockIO(sock, false); uint64 lastThrowTime = 0; uint8 ball = 'B'; // this is what we throw back and forth over the TCP socket! uint64 min=((uint64)-1), max=0; uint64 lastPrintTime = 0; uint64 count = 0; uint64 total = 0; SocketMultiplexer multiplexer; while(1) { int fd = sock.GetFileDescriptor(); multiplexer.RegisterSocketForReadReady(fd); if (myTurnToThrow) multiplexer.RegisterSocketForWriteReady(fd); if (multiplexer.WaitForEvents() < 0) { LogTime(MUSCLE_LOG_ERROR, "WaitForEvents() failed, aborting!\n"); break; } if ((myTurnToThrow)&&(multiplexer.IsSocketReadyForWrite(fd))) { int32 bytesWritten = sockIO.Write(&ball, sizeof(ball)); if (bytesWritten == sizeof(ball)) { if (doFlush) sockIO.FlushOutput(); // nagle's algorithm gets toggled here! lastThrowTime = GetRunTime64(); myTurnToThrow = false; // we thew the ball, now wait to catch it again! } else if (bytesWritten < 0) { LogTime(MUSCLE_LOG_ERROR, "Error sending ball, aborting!\n"); break; } } if (multiplexer.IsSocketReadyForRead(fd)) { int32 bytesRead = sockIO.Read(&ball, sizeof(ball)); if (bytesRead == sizeof(ball)) { if (myTurnToThrow == false) { if (lastThrowTime > 0) { uint64 elapsedTime = GetRunTime64() - lastThrowTime; count++; total += elapsedTime; min = muscleMin(min, elapsedTime); max = muscleMax(max, elapsedTime); if (OnceEvery(MICROS_PER_SECOND, lastPrintTime)) LogTime(MUSCLE_LOG_INFO, "count=" UINT64_FORMAT_SPEC" min=" UINT64_FORMAT_SPEC "us max=" UINT64_FORMAT_SPEC "us avg=" UINT64_FORMAT_SPEC "us\n", count, min, max, total/count); } myTurnToThrow = true; // we caught the ball, now throw it back! } } else if (bytesRead < 0) { LogTime(MUSCLE_LOG_ERROR, "Error reading ball, aborting!\n"); break; } } } }
// This program tests the SocketMultiplexer class by seeing how many chained socket-pairs // we can chain a message through sequentially int main(int argc, char ** argv) { CompleteSetupSystem css; uint32 numPairs = 5; if (argc > 1) numPairs = atoi(argv[1]); bool quiet = false; if ((argc > 2)&&(strcmp(argv[2], "quiet") == 0)) quiet = true; #ifdef __APPLE__ // Tell MacOS/X that yes, we really do want to create this many file descriptors struct rlimit rl; rl.rlim_cur = rl.rlim_max = (numPairs*2)+5; if (setrlimit(RLIMIT_NOFILE, &rl) != 0) perror("setrlimit"); #endif printf("Testing %i socket-pairs chained together...\n", numPairs); Queue<ConstSocketRef> senders; (void) senders.EnsureSize(numPairs, true); Queue<ConstSocketRef> receivers; (void) receivers.EnsureSize(numPairs, true); for (uint32 i=0; i<numPairs; i++) { if (CreateConnectedSocketPair(senders[i], receivers[i]) != B_NO_ERROR) { printf("Error, failed to create socket pair #" UINT32_FORMAT_SPEC "!\n", i); return 10; } } // Start the game off char c = 'C'; if (SendData(senders[0], &c, 1, false) != 1) { printf("Error, couldn't send initial byte!\n"); return 10; } uint64 count = 0; uint64 tally = 0; uint64 minRunTime = (uint64)-1; uint64 maxRunTime = 0; SocketMultiplexer multiplexer; uint64 endTime = GetRunTime64() + SecondsToMicros(10); bool error = false; while(error==false) { for (uint32 i=0; i<numPairs; i++) { if (multiplexer.RegisterSocketForReadReady(receivers[i].GetFileDescriptor()) != B_NO_ERROR) { printf("Error, RegisterSocketForRead() failed for receiver #" UINT32_FORMAT_SPEC "!\n", i); error = true; break; } } if (error) break; uint64 then = GetRunTime64(); if (then >= endTime) break; int ret = multiplexer.WaitForEvents(); if (ret < 0) { printf("WaitForEvents errored out, aborting test!\n"); break; } uint64 elapsed = GetRunTime64()-then; if (quiet == false) printf("WaitForEvents returned %i after " UINT64_FORMAT_SPEC " microseconds.\n", ret, elapsed); count++; tally += elapsed; minRunTime = muscleMin(minRunTime, elapsed); maxRunTime = muscleMax(maxRunTime, elapsed); for (uint32 i=0; i<numPairs; i++) { if (multiplexer.IsSocketReadyForRead(receivers[i].GetFileDescriptor())) { char buf[64]; int32 numBytesReceived = ReceiveData(receivers[i], buf, sizeof(buf), false); if (quiet == false) printf("Receiver #" UINT32_FORMAT_SPEC " signalled ready-for-read, read " INT32_FORMAT_SPEC " bytes.\n", i, numBytesReceived); if (numBytesReceived > 0) { uint32 nextIdx = (i+1)%numPairs; int32 sentBytes = SendData(senders[nextIdx], buf, numBytesReceived, false); if (quiet == false) printf("Sent " INT32_FORMAT_SPEC " bytes on sender #" UINT32_FORMAT_SPEC "\n", sentBytes, nextIdx); } } } } printf("Test complete: WaitEvents() called " UINT64_FORMAT_SPEC " times, averageTime=" UINT64_FORMAT_SPEC "uS, minimumTime=" UINT64_FORMAT_SPEC "uS, maximumTime=" UINT64_FORMAT_SPEC "uS.\n", count, tally/(count?count:1), minRunTime, maxRunTime); return 0; }