int main (int argc, char **argv) { OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n" "(transport build: OTMessage -> TCP -> SSL)\n" "NOTE: IF YOU PREFER TO USE XmlRpc with Http transport, then rebuild using:\n" "\"make -f Makefile.rpc\" (but make sure the client is built the same way.)\n\n", OTLog::Version()); // ----------------------------------------------------------------------- // This object handles all the actual transaction notarization details. // (This file you are reading is a wrapper for OTServer, which adds the transport layer.) OTServer theServer; // ----------------------------------------------------------------------- #ifdef _WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD( 2, 2 ); int nWSA = WSAStartup( wVersionRequested, &wsaData ); OT_ASSERT_MSG(0 != nWSA, "Error calling WSAStartup.\n"); #endif OTLog::vOutput(0, "\n\nWelcome to Open Transactions, version %s.\n\n", OTLog::Version()); // ----------------------------------------------------------------------- OTString strCAFile, strDHFile, strKeyFile, strSSLPassword; if (argc < 2) { OTLog::vOutput(0, "Usage: transaction <SSL-password> <data_folder path>\n\n" "(Password defaults to '%s' if left blank on the command line.)\n" "(Folder defaults to '%s' if left blank.)\n", KEY_PASSWORD, SERVER_PATH_DEFAULT); strSSLPassword.Set(KEY_PASSWORD); OTLog::SetMainPath(SERVER_PATH_DEFAULT); } else if (argc < 3) { OTLog::vOutput(0, "Usage: transaction <SSL-password> <data_folder path>\n\n" "(Folder defaults to '%s' if left blank.)\n", SERVER_PATH_DEFAULT); strSSLPassword.Set(argv[1]); OTLog::SetMainPath(SERVER_PATH_DEFAULT); } else { strSSLPassword.Set(argv[1]); OTLog::SetMainPath(argv[2]); } strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE); strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE); strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE); // ----------------------------------------------------------------------- // Init loads up server's nym so it can decrypt messages sent in envelopes. // It also does various other initialization work. // // (Envelopes prove that ONLY someone who actually had the server contract, // and had loaded it into his wallet, could ever connect to the server or // communicate with it. And if that person is following the contract, there // is only one server he can connect to, and one key he can use to talk to it.) OTLog::Output(0, "\n\nNow loading the server nym, which will also ask you for a password, to unlock\n" "its private key. (Default password is \"test\".)\n"); // Initialize SSL -- This MUST occur before any Private Keys are loaded! SFSocketGlobalInit(); // Any app that uses OT has to initialize SSL first. theServer.Init(); // Keys, etc are loaded here. // ----------------------------------------------------------------------- // We're going to listen on the same port that is listed in our server contract. // // OTString strHostname; // The hostname of this server, according to its own contract. int nPort=0; // The port of this server, according to its own contract. OT_ASSERT_MSG(theServer.GetConnectInfo(strHostname, nPort), "Unable to find my own connect info (which is in my server contract BTW.)\n"); const int nServerPort = nPort; // ----------------------------------------------------------------------- SFSocket *socket = NULL; listOfConnections theConnections; // Alloc Socket socket = SFSocketAlloc(); OT_ASSERT_MSG(NULL != socket, "SFSocketAlloc Failed\n"); // Initialize SSL Socket int nSFSocketInit = SFSocketInit(socket, strCAFile.Get(), strDHFile.Get(), strKeyFile.Get(), strSSLPassword.Get(), NULL); OT_ASSERT_MSG(nSFSocketInit >= 0, "SFSocketInit Context Failed\n"); // Listen on Address/Port int nSFSocketListen = SFSocketListen(socket, INADDR_ANY, nServerPort); OT_ASSERT_MSG(nSFSocketListen >= 0, "nSFSocketListen Failed\n"); theServer.ActivateCron(); do { SFSocket * clientSocket = NULL; // Accept Client Connection if (NULL != (clientSocket = SFSocketAccept(socket))) { OTClientConnection * pClient = new OTClientConnection(*clientSocket, theServer); theConnections.push_back(pClient); OTLog::Output(0, "Accepting new connection.\n"); } // READ THROUGH ALL CLIENTS HERE, LOOP A LIST // AND process in-buffer onto our list of messages. OTClientConnection * pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { // Here we read the bytes from the pipe into the client's buffer // As necessary this function also processes those bytes into OTMessages // and adds them to the Input List for that client. pConnection->ProcessBuffer(); } } // Now loop through them all again, and process their messages onto the reply list. pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextInputMessage()) { OTMessage * pReply = new OTMessage; OT_ASSERT(NULL != pReply); if (theServer.ProcessUserCommand(*pMsg, *pReply, pConnection)) { OTLog::vOutput(0, "Successfully processed user command: %s.\n", pMsg->m_strCommand.Get()); pConnection->AddToOutputList(*pReply); } else { OTLog::Output(0, "Unable to process user command in XML, or missing payload, in main.\n"); delete pReply; pReply = NULL; } // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // Now loop through them all again, and process their replies down the pipe pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextOutputMessage()) { pConnection->ProcessReply(*pMsg); // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // The Server now processes certain things on a regular basis. // This method call is what gives it the opportunity to do that. theServer.ProcessCron(); // Now go to sleep for a tenth of a second. // (Main loop processes ten times per second, currently.) OTLog::SleepMilliseconds(100); // 100 ms == (1 second / 10) } while (1); // Close and Release Socket Resources SFSocketRelease(socket); #ifdef _WIN32 WSACleanup(); #endif return(0); }
int main (int argc, char **argv) { OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n" "(transport build: OTMessage -> TCP -> SSL)\n" "IF YOU PREFER TO USE XmlRpc with HTTP, then rebuild from main folder like this:\n\n" "cd ..; make clean; make rpc\n\n", OTLog::Version()); // ----------------------------------------------------------------------- // This object handles all the actual transaction notarization details. // (This file you are reading is a wrapper for OTServer, which adds the transport layer.) OTServer theServer; // ----------------------------------------------------------------------- #ifdef _WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD( 2, 2 ); int err = WSAStartup( wVersionRequested, &wsaData ); /* Tell the user that we could not find a usable */ /* Winsock DLL. */ OT_ASSERT_MSG((err == 0), "WSAStartup failed!\n"); /* Confirm that the WinSock DLL supports 2.2. */ /* Note that if the DLL supports versions greater */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we */ /* requested. */ bool bWinsock = (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2); /* Tell the user that we could not find a usable */ /* WinSock DLL. */ if (!bWinsock) WSACleanup(); // do cleanup. OT_ASSERT_MSG((!bWinsock), "Could not find a usable version of Winsock.dll\n"); /* The Winsock DLL is acceptable. Proceed to use it. */ /* Add network programming using Winsock here */ /* then call WSACleanup when done using the Winsock dll */ OTLog::vOutput(0,"The Winsock 2.2 dll was found okay\n"); #endif OTLog::vOutput(0, "\n\nWelcome to Open Transactions, version %s.\n\n", OTLog::Version()); // ----------------------------------------------------------------------- // The beginnings of an INI file!! #ifndef _WIN32 // if UNIX (NOT windows) wordexp_t exp_result; wordexp(OT_INI_FILE_DEFAULT, &exp_result, 0); const OTString strIniFileDefault(exp_result.we_wordv[0]); wordfree(&exp_result); #else const OTString strIniFileDefault(OT_INI_FILE_DEFAULT); #endif OTString strPath(SERVER_PATH_DEFAULT); { CSimpleIniA ini; SI_Error rc = ini.LoadFile(strIniFileDefault.Get()); if (rc >=0) { const char * pVal = ini.GetValue("paths", "server_path", SERVER_PATH_DEFAULT); // todo stop hardcoding. if (NULL != pVal) { strPath.Set(pVal); OTLog::vOutput(0, "Reading ini file (%s). \n Found Server data_folder path: %s \n", strIniFileDefault.Get(), strPath.Get()); } else { strPath.Set(SERVER_PATH_DEFAULT); OTLog::vOutput(0, "Reading ini file (%s): \n Failed reading Server data_folder path. Using: %s \n", strIniFileDefault.Get(), strPath.Get()); } } else { strPath.Set(SERVER_PATH_DEFAULT); OTLog::vOutput(0, "Unable to load ini file (%s) to find data_folder path\n Will assume that server data_folder is at path: %s \n", strIniFileDefault.Get(), strPath.Get()); } } // ----------------------------------------------------------------------- OTString strCAFile, strDHFile, strKeyFile; //, strSSLPassword; if (argc < 1) { OTLog::vOutput(0, "\n==> USAGE: %s [absolute_path_to_data_folder]\n\n" // OTLog::vOutput(0, "\n==> USAGE: %s <SSL-password> [absolute_path_to_data_folder]\n\n" #if defined (FELLOW_TRAVELER) // "(Password defaults to '%s' if left blank.)\n" "(Folder defaults to '%s' if left blank.)\n" #else "The test password is always 'test'.\n" "OT will try to read the data_folder path from your ini file. If you prefer\n" "to specify it at the command line, and you want to see the exact path, type:\n" " cd data_folder && pwd && cd ..\n" #endif "\n\n", argv[0] #if defined (FELLOW_TRAVELER) // , KEY_PASSWORD, , strPath.Get() #endif ); #if defined (FELLOW_TRAVELER) // strSSLPassword.Set(KEY_PASSWORD); OTLog::SetMainPath(strPath.Get()); #else exit(1); #endif } else if (argc < 2) { // strSSLPassword.Set(argv[1]); OTLog::SetMainPath(strPath.Get()); } else { // strSSLPassword.Set(argv[1]); OTLog::SetMainPath(argv[1]); // formerly [2] } OTLog::vOutput(0, "Using as path to data folder: %s\n", OTLog::Path()); strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE); strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE); strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE); // ----------------------------------------------------------------------- // Init loads up server's nym so it can decrypt messages sent in envelopes. // It also does various other initialization work. // // (Envelopes prove that ONLY someone who actually had the server contract, // and had loaded it into his wallet, could ever connect to the server or // communicate with it. And if that person is following the contract, there // is only one server he can connect to, and one key he can use to talk to it.) OTLog::Output(0, "\n\nNow loading the server nym, which will also ask you for a password, to unlock\n" "its private key. (Default password is \"test\".)\n"); // Initialize SSL -- This MUST occur before any Private Keys are loaded! SFSocketGlobalInit(); // Any app that uses OT has to initialize SSL first. theServer.Init(); // Keys, etc are loaded here. // ----------------------------------------------------------------------- // We're going to listen on the same port that is listed in our server contract. // // OTString strHostname; // The hostname of this server, according to its own contract. int nPort=0; // The port of this server, according to its own contract. OT_ASSERT_MSG(theServer.GetConnectInfo(strHostname, nPort), "Unable to find my own connect info (which is in my server contract BTW.)\n"); const int nServerPort = nPort; // ----------------------------------------------------------------------- SFSocket *socket = NULL; listOfConnections theConnections; // Alloc Socket socket = SFSocketAlloc(); OT_ASSERT_MSG(NULL != socket, "SFSocketAlloc Failed\n"); // Initialize SSL Socket int nSFSocketInit = SFSocketInit(socket, strCAFile.Get(), strDHFile.Get(), strKeyFile.Get(), strSSLPassword.Get(), NULL); OT_ASSERT_MSG(nSFSocketInit >= 0, "SFSocketInit Context Failed\n"); // Listen on Address/Port int nSFSocketListen = SFSocketListen(socket, INADDR_ANY, nServerPort); OT_ASSERT_MSG(nSFSocketListen >= 0, "nSFSocketListen Failed\n"); theServer.ActivateCron(); do { SFSocket * clientSocket = NULL; // Accept Client Connection if (NULL != (clientSocket = SFSocketAccept(socket))) { OTClientConnection * pClient = new OTClientConnection(*clientSocket, theServer); theConnections.push_back(pClient); OTLog::Output(0, "Accepting new connection.\n"); } // READ THROUGH ALL CLIENTS HERE, LOOP A LIST // AND process in-buffer onto our list of messages. OTClientConnection * pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { // Here we read the bytes from the pipe into the client's buffer // As necessary this function also processes those bytes into OTMessages // and adds them to the Input List for that client. pConnection->ProcessBuffer(); } } // Now loop through them all again, and process their messages onto the reply list. pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextInputMessage()) { OTMessage * pReply = new OTMessage; OT_ASSERT(NULL != pReply); if (theServer.ProcessUserCommand(*pMsg, *pReply, pConnection)) { OTLog::vOutput(0, "Successfully processed user command: %s.\n", pMsg->m_strCommand.Get()); pConnection->AddToOutputList(*pReply); } else { OTLog::Output(0, "Unable to process user command in XML, or missing payload, in main.\n"); delete pReply; pReply = NULL; } // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // Now loop through them all again, and process their replies down the pipe pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextOutputMessage()) { pConnection->ProcessReply(*pMsg); // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // The Server now processes certain things on a regular basis. // This method call is what gives it the opportunity to do that. theServer.ProcessCron(); // Now go to sleep for a tenth of a second. // (Main loop processes ten times per second, currently.) OTLog::SleepMilliseconds(100); // 100 ms == (1 second / 10) } while (1); // Close and Release Socket Resources SFSocketRelease(socket); #ifdef _WIN32 WSACleanup(); #endif return(0); }
// ********************************************************************************************************* // // // *** SERVER MAIN *** // // // The MAIN function for the server software, which starts up the ZMQ listener, as well // as well as the Open Transactions library and the OT Server object. // // After initialization, this function becomes the "main loop" of OT server. // int main(int argc, char* argv[]) { OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n" "(transport build: OTMessage -> OTEnvelope -> ZMQ )\n\n", OTLog::Version()); // WINSOCK WINDOWS // ----------------------------------------------------------------------- #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; int err; /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { /* Tell the user that we could not find a usable */ /* Winsock DLL. */ printf("WSAStartup failed with error: %d\n", err); return 1; } /* Confirm that the WinSock DLL supports 2.2. */ /* Note that if the DLL supports versions greater */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we */ /* requested. */ if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ printf("Could not find a usable version of Winsock.dll\n"); WSACleanup(); return 1; } else printf("The Winsock 2.2 dll was found okay\n"); /* The Winsock DLL is acceptable. Proceed to use it. */ /* Add network programming using Winsock here */ /* then call WSACleanup when done using the Winsock dll */ #endif // *********************************************************************** // INITIALIZATION and CLEANUP (for the OT library, and for this server application.) // class __ot_server_ { OTServer * m_pServer; public: OTServer * GetServer() { return m_pServer; } // ----------------------------------- __ot_server_() : m_pServer(NULL) // INIT { // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- // OTLog class exists on both client and server sides. // #define OT_NO_SIGNAL_HANDLING if you want to turn off OT's signal handling. // #if defined(OT_SIGNAL_HANDLING) OTLog::SetupSignalHandler(); // This is optional! (I, of course, am using it in this test app...) #endif // ----------------------------------------------------------------------- // I instantiate this here (instead of globally) so that I am assured that any globals and other // setup is already done before we instantiate the server object itself. // OT_ASSERT_MSG(NULL == m_pServer, "server main(): ASSERT: NULL == m_pServer."); m_pServer = new OTServer; // // (This .cpp file you are currently reading is a wrapper for OTServer, // which adds the transport layer.) // OT_ASSERT_MSG(NULL != m_pServer, "server main(): ASSERT: Unable to instantiate OT server.\n"); OTString pathUserAppDataPath, pathIniFileLocation; pathUserAppDataPath = GetRoamingAppDataLocation(); pathIniFileLocation.Format("%s%s%s", pathUserAppDataPath.Get(), OTLog::PathSeparator(), SERVER_INI_FILE_DEFAULT); OTString pathOTServerDataLocation; OTLog::vOutput(0, "\nFound ot_init.cfg in: \n %s \nNow checking to see if it contains the OT Server path...", pathIniFileLocation.Get()); // Read the File, If successful use result if (false == GetOTAppDataFolderLocation(pathIniFileLocation, pathOTServerDataLocation)) { OTLog::vOutput(0, "Path not found... Will attempt default!... \n"); // Not successfull will will assume it is in default location: pathOTServerDataLocation.Format("%s%s%s", pathUserAppDataPath.Get(), OTLog::PathSeparator(), SERVER_PATH_DEFAULT); }; OTLog::vOutput(0, " %s \n", pathOTServerDataLocation.Get()); OTLog::SetMainPath(pathOTServerDataLocation.Get()); // <============ SET MAIN PATH OTLog::vOutput(0, "Using server_data path: %s\n", OTLog::Path()); // ----------------------------------------------------------------------- OTCrypto::It()->Init(); // <========== (OpenSSL gets initialized here.) } // **************************************** // ~__ot_server_() // CLEANUP { OTLog::vOutput(0, "\n\n OT version %s, shutting down and cleaning up.\n", OTLog::Version()); // ------------------------------ if (NULL != m_pServer) delete m_pServer; m_pServer = NULL; // ------------------------------ // We clean these up in reverse order from the Init function, which just seems // like the best default, in absence of any brighter ideas. // OTCrypto::It()->Cleanup(); // <======= (OpenSSL gets cleaned up here.) // ------------------------- // (This is at the bottom, since we do the cleanup in the // reverse order from initialization.) #ifdef _WIN32 WSACleanup(); #endif } }; // *********************************************************************** // // INSTANTIATE and INITIALIZE... // // (Cleanup happens automatically when this object goes out of scope.) // __ot_server_ the_server_obj; OTServer * pServer = the_server_obj.GetServer(); OT_ASSERT(NULL != pServer); // ----------------------------------------------------------------------- // OTString strCAFile, strDHFile, strKeyFile; //, strSSLPassword; // strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE); // strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE); // strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE); // ----------------------------------------------------------------------- // // UPDATE: This was moved to OTLog::OT_Init(), which is called above, by the // nested cleanup class. // // Initialize SSL -- This MUST occur before any Private Keys are loaded! // SSL_library_init(); // SSL_load_error_strings(); // ----------------------------------------------------------------------- // OTServer::Init loads up server's nym so it can decrypt messages sent in // envelopes. It also does various other initialization work. // // (Envelopes prove that ONLY someone who actually had the server contract, // and had loaded it into his wallet, could ever connect to the server or // communicate with it. And if that person is following the contract, there // is only one server he can connect to, and one key he can use to talk to it.) // OTLog::vOutput(0, "\nNow loading the server nym, which will also ask you for a password, to unlock\n" "its private key. (Default password is \"%s\".)\n", KEY_PASSWORD); pServer->Init(); // Keys, etc are loaded here. ===> Assumes main path is set! <=== // ----------------------------------------------------------------------- // We're going to listen on the same port that is listed in our server contract. // // OTString strHostname; // The hostname of this server, according to its own contract. int nPort=0; // The port of this server, according to its own contract. const bool bConnectInfo = pServer->GetConnectInfo(strHostname, nPort); OT_ASSERT_MSG(bConnectInfo, "server main: Unable to find my own connect info (which SHOULD be in my server contract, BTW.) Perhaps you failed trying to open that contract? Have you tried the test password? (\"test\")\n"); const int nServerPort = nPort; // ----------------------------------------------------------------------- // OT CRON // // A heartbeat for recurring transactions, such as markets, payment plans, // and smart contracts. pServer->ActivateCron(); // NOTE: Currently we trigger OT Cron's processing internally, but there's no reason why, in the // future, we can't make an actual cron job that triggers a script, that fires a message // to OT, causing OT to process its Cron (even if we were single-threaded we could do this...) // // Have to put some thought into it... // // Wouldn't require much security, since OT can still be smart enough not to process cron any // more often than X minutes, no matter HOW many times the ProcessCron script fires. // Thing is, though, that with this setup, we can't really guarantee that cron will EVER be // triggered -- whereas the way OT is now, at least we know it WILL fire every X seconds. // // -------------------------------------- // // NETWORK // // Prepare our context and listening socket... OTSocket theSocket; OTString strBindPath; strBindPath.Format("%s%d", "tcp://*:", nServerPort); theSocket.Listen(strBindPath); // ****************************************************************************************** // // *** MAIN LOOP *** // do // --------------------------- { // =-=-=- HEARTBEAT -=-=-= // // The Server now processes certain things on a regular basis. // ProcessCron is what gives it the opportunity to do that. // All of the Cron Items (including market trades, payment plans, smart contracts...) // they all have their hooks here... // pServer->ProcessCron(); // Internally this is smart enough to know how often to actually activate itself. // Most often it just returns doing nothing (waiting for its timer.) // ----------------------------------------------------------------------- // Wait for client http requests (and process replies out to them.) // ---------------------------------------------------------------------- // Number of requests to process per heartbeat: OTServer::GetHeartbeatNoRequests() // // Loop: process up to 10 client requests, then sleep for 1/10th second. // That's a total of 100 requests per second. Can the computers handle it? // Is it too much or too little? Todo: load testing. // // Then: check for shutdown flag. // // Then: go back to the top ("do") and repeat the loop.... process cron, // process 10 client requests, sleep, check for shutdown, etc. // // Timer t; // start timer t.start(); const double tick1 = t.getElapsedTimeInMilliSec(); // ----------------------------------------------------- // // PROCESS X NUMBER OF REQUESTS (THIS PULSE.) // // Theoretically the "number of requests" that we process EACH PULSE. // (The timing code here is still pretty new, need to do some load testing.) // for (int i = 0; i < /*10*/OTServer::GetHeartbeatNoRequests(); i++) { std::string str_Message; // With 100ms heartbeat, receive will try 100 ms, then 200 ms, then 400 ms, total of 700. // That's about 15 Receive() calls every 10 seconds. Therefore if I want the ProcessCron() // to trigger every 10 seconds, I need to set the cron interval to roll over every 15 heartbeats. // Therefore I will be using a real Timer for Cron, instead of the damn intervals. // bool bReceived = theSocket.Receive(str_Message); if (bReceived) { std::string str_Reply; // Output. if (str_Message.length() <= 0) { OTLog::Error("server main: Received a message, but of 0 length or less. Weird. (Skipping it.)\n"); } else // ------------------------------------ { // true == YES, DISCONNECT m_pSocket, something must have gone wrong. // false == NO, do NOT disconnect m_pSocket, everything went wonderfully! // const bool bShouldDisconnect = ProcessMessage_ZMQ(*pServer, str_Message, str_Reply); // <================== PROCESS the message! // -------------------------------------------------- if ((str_Reply.length() <= 0) || bShouldDisconnect) { OTLog::vOutput(0, "server main: ERROR: Unfortunately, not every client request is " "legible or worthy of a server response. :-) " "Msg:\n\n%s\n\n", str_Message.c_str()); theSocket.Listen(); } else { bool bSuccessSending = theSocket.Send(str_Reply); // <===== SEND THE REPLY if (false == bSuccessSending) OTLog::vError("server main: Socket ERROR: failed while trying to send reply " "back to client! \n\n MESSAGE:\n%s\n\nREPLY:\n%s\n\n", str_Message.c_str(), str_Reply.c_str()); // -------------------------------------------------- } } } } // for // ----------------------------------------------------------------------- // // IF the time we had available wasn't all used up -- if some of it is still // available, then SLEEP until we reach the NEXT PULSE. (In practice, we will // probably use TOO MUCH time, not too little--but then again OT isn't ALWAYS // processing a message. There could be plenty of dead time in between...) // const double tick2 = t.getElapsedTimeInMilliSec(); const long elapsed = static_cast<long>(tick2 - tick1); long lSleepMS = 0; if (elapsed < /*100*/OTServer::GetHeartbeatMsBetweenBeats()) { lSleepMS = OTServer::GetHeartbeatMsBetweenBeats() - elapsed; // Now go to sleep. // (The main loop processes ten times per second, currently.) OTLog::SleepMilliseconds(lSleepMS); // 100 ms == (1 second / 10) } // ----------------------------------------------------------------------- // ARTIFICIAL LIMIT: // 10 requests per heartbeat, 10 rounds per second == 100 requests per second. // // *** ONE HUNDRED CLIENT MESSAGES PER SECOND is the same as: // // 6000 PER MINUTE == 360,000 PER HOUR == 8,640,000 PER DAY*** // // Speeding it up is just a matter of adjusting the above numbers, and LOAD TESTING, // to see if OT can handle it. (Not counting optimization of course.) // // ----------------------------------------------------------------------- if (pServer->IsFlaggedForShutdown()) { OTLog::Output(0, "main: OT Server is shutting down gracefully....\n"); break; } } while (1); // (MAIN LOOP) // ------------------------------------ return 0; }