// // EchoServer.cpp // // $Id: //poco/1.3/Net/samples/EchoServer/src/EchoServer.cpp#1 $ // // This sample demonstrates the SocketReactor and SocketAcceptor classes. // // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Poco/Net/SocketReactor.h" #include "Poco/Net/SocketAcceptor.h" #include "Poco/Net/SocketNotification.h" #include "Poco/Net/StreamSocket.h" #include "Poco/Net/ServerSocket.h" #include "Poco/NObserver.h" #include "Poco/Exception.h" #include "Poco/Thread.h" #include "Poco/Util/ServerApplication.h" #include "Poco/Util/Option.h" #include "Poco/Util/OptionSet.h" #include "Poco/Util/HelpFormatter.h" #include using Poco::Net::SocketReactor; using Poco::Net::SocketAcceptor; using Poco::Net::ReadableNotification; using Poco::Net::ShutdownNotification; using Poco::Net::ServerSocket; using Poco::Net::StreamSocket; using Poco::NObserver; using Poco::AutoPtr; using Poco::Thread; using Poco::Util::ServerApplication; using Poco::Util::Application; using Poco::Util::Option; using Poco::Util::OptionSet; using Poco::Util::HelpFormatter; class EchoServiceHandler { public: EchoServiceHandler(StreamSocket& socket, SocketReactor& reactor): _socket(socket), _reactor(reactor), _pBuffer(new char[BUFFER_SIZE]) { // Application& app = Application::instance(); // app.logger().information("Connection from " + socket.peerAddress().toString()); _reactor.addEventHandler(_socket, NObserver(*this, &EchoServiceHandler::onReadable)); _reactor.addEventHandler(_socket, NObserver(*this, &EchoServiceHandler::onShutdown)); } ~EchoServiceHandler() { // Application& app = Application::instance(); // app.logger().information("Disconnecting " + _socket.peerAddress().toString()); _reactor.removeEventHandler(_socket, NObserver(*this, &EchoServiceHandler::onReadable)); _reactor.removeEventHandler(_socket, NObserver(*this, &EchoServiceHandler::onShutdown)); delete [] _pBuffer; } void onReadable(const AutoPtr& pNf) { int n = _socket.receiveBytes(_pBuffer, BUFFER_SIZE); if (n > 0) _socket.sendBytes(_pBuffer, n); else delete this; } void onShutdown(const AutoPtr& pNf) { delete this; } private: enum { BUFFER_SIZE = 1024 }; StreamSocket _socket; SocketReactor& _reactor; char* _pBuffer; }; class EchoServer: public Poco::Util::ServerApplication /// The main application class. /// /// This class handles command-line arguments and /// configuration files. /// Start the EchoServer executable with the help /// option (/help on Windows, --help on Unix) for /// the available command line options. /// /// To use the sample configuration file (EchoServer.properties), /// copy the file to the directory where the EchoServer executable /// resides. If you start the debug version of the EchoServer /// (EchoServerd[.exe]), you must also create a copy of the configuration /// file named EchoServerd.properties. In the configuration file, you /// can specify the port on which the server is listening (default /// 9977) and the format of the date/time string sent back to the client. /// /// To test the EchoServer you can use any telnet client (telnet localhost 9977). { public: EchoServer(): _helpRequested(false) { } ~EchoServer() { } protected: void initialize(Application& self) { loadConfiguration(); // load default configuration files, if present ServerApplication::initialize(self); } void uninitialize() { ServerApplication::uninitialize(); } void defineOptions(OptionSet& options) { ServerApplication::defineOptions(options); options.addOption( Option("help", "h", "display help information on command line arguments") .required(false) .repeatable(false)); } void handleOption(const std::string& name, const std::string& value) { ServerApplication::handleOption(name, value); if (name == "help") _helpRequested = true; } void displayHelp() { HelpFormatter helpFormatter(options()); helpFormatter.setCommand(commandName()); helpFormatter.setUsage("OPTIONS"); helpFormatter.setHeader("An echo server implemented using the Reactor and Acceptor patterns."); helpFormatter.format(std::cout); } int main(const std::vector& args) { if (_helpRequested) { displayHelp(); } else { if (args.size() < 1) { printf("Usage: cmd port\n"); return -10; } int port = atoi(args[0].c_str()); // set-up a server socket ServerSocket svs(port); // set-up a SocketReactor... SocketReactor reactor; // ... and a SocketAcceptor SocketAcceptor acceptor(svs, reactor); // run the reactor in its own thread so that we can wait for // a termination request Thread thread; thread.start(reactor); // wait for CTRL-C or kill waitForTerminationRequest(); // Stop the SocketReactor reactor.stop(); thread.join(); } return Application::EXIT_OK; } private: bool _helpRequested; }; int main(int argc, char** argv) { EchoServer app; return app.run(argc, argv); }