|
|
@@ -1,4 +1,36 @@
|
|
|
-// @see poco/Net/samples/EchoServer
|
|
|
+//
|
|
|
+// 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"
|
|
|
@@ -8,186 +40,186 @@
|
|
|
#include "Poco/NObserver.h"
|
|
|
#include "Poco/Exception.h"
|
|
|
#include "Poco/Thread.h"
|
|
|
-#include "Poco/FIFOBuffer.h"
|
|
|
-#include "Poco/Delegate.h"
|
|
|
#include "Poco/Util/ServerApplication.h"
|
|
|
#include "Poco/Util/Option.h"
|
|
|
#include "Poco/Util/OptionSet.h"
|
|
|
#include "Poco/Util/HelpFormatter.h"
|
|
|
#include <iostream>
|
|
|
|
|
|
+
|
|
|
using Poco::Net::SocketReactor;
|
|
|
using Poco::Net::SocketAcceptor;
|
|
|
using Poco::Net::ReadableNotification;
|
|
|
-using Poco::Net::WritableNotification;
|
|
|
using Poco::Net::ShutdownNotification;
|
|
|
using Poco::Net::ServerSocket;
|
|
|
using Poco::Net::StreamSocket;
|
|
|
using Poco::NObserver;
|
|
|
using Poco::AutoPtr;
|
|
|
using Poco::Thread;
|
|
|
-using Poco::FIFOBuffer;
|
|
|
-using Poco::delegate;
|
|
|
using Poco::Util::ServerApplication;
|
|
|
using Poco::Util::Application;
|
|
|
using Poco::Util::Option;
|
|
|
using Poco::Util::OptionSet;
|
|
|
using Poco::Util::HelpFormatter;
|
|
|
|
|
|
+
|
|
|
class EchoServiceHandler
|
|
|
- /// I/O handler class. This class (un)registers handlers for I/O based on
|
|
|
- /// data availability. To ensure non-blocking behavior and alleviate spurious
|
|
|
- /// socket writability callback triggering when no data to be sent is available,
|
|
|
- /// FIFO buffers are used. I/O FIFOBuffer sends notifications on transitions
|
|
|
- /// from [1] non-readable (i.e. empty) to readable, [2] writable to non-writable
|
|
|
- /// (i.e. full) and [3] non-writable (i.e. full) to writable.
|
|
|
- /// Based on these notifications, the handler member functions react by
|
|
|
- /// enabling/disabling respective reactor framework notifications.
|
|
|
{
|
|
|
public:
|
|
|
- EchoServiceHandler(StreamSocket& socket, SocketReactor& reactor):
|
|
|
- _socket(socket),
|
|
|
- _reactor(reactor),
|
|
|
- _fifoIn(BUFFER_SIZE, true),
|
|
|
- _fifoOut(BUFFER_SIZE, true)
|
|
|
- {
|
|
|
- _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
|
|
|
- _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ShutdownNotification>(*this, &EchoServiceHandler::onSocketShutdown));
|
|
|
-
|
|
|
- _fifoOut.readable += delegate(this, &EchoServiceHandler::onFIFOOutReadable);
|
|
|
- _fifoIn.writable += delegate(this, &EchoServiceHandler::onFIFOInWritable);
|
|
|
- }
|
|
|
-
|
|
|
- ~EchoServiceHandler()
|
|
|
- {
|
|
|
- _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
|
|
|
- _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, WritableNotification>(*this, &EchoServiceHandler::onSocketWritable));
|
|
|
- _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ShutdownNotification>(*this, &EchoServiceHandler::onSocketShutdown));
|
|
|
-
|
|
|
- _fifoOut.readable -= delegate(this, &EchoServiceHandler::onFIFOOutReadable);
|
|
|
- _fifoIn.writable -= delegate(this, &EchoServiceHandler::onFIFOInWritable);
|
|
|
- }
|
|
|
-
|
|
|
- void onFIFOOutReadable(bool& b)
|
|
|
- {
|
|
|
- if (b)
|
|
|
- _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, WritableNotification>(*this, &EchoServiceHandler::onSocketWritable));
|
|
|
- else
|
|
|
- _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, WritableNotification>(*this, &EchoServiceHandler::onSocketWritable));
|
|
|
- }
|
|
|
-
|
|
|
- void onFIFOInWritable(bool& b)
|
|
|
- {
|
|
|
- if (b)
|
|
|
- _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
|
|
|
- else
|
|
|
- _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
|
|
|
- }
|
|
|
-
|
|
|
- void onSocketReadable(const AutoPtr<ReadableNotification>& pNf)
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- int len = _socket.receiveBytes(_fifoIn);
|
|
|
- if (len > 0)
|
|
|
- {
|
|
|
- _fifoIn.drain(_fifoOut.write(_fifoIn.buffer(), _fifoIn.used()));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- delete this;
|
|
|
- }
|
|
|
- }
|
|
|
- catch (Poco::Exception& exc)
|
|
|
- {
|
|
|
- Application& app = Application::instance();
|
|
|
- app.logger().log(exc);
|
|
|
- delete this;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void onSocketWritable(const AutoPtr<WritableNotification>& pNf)
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- _socket.sendBytes(_fifoOut);
|
|
|
- }
|
|
|
- catch (Poco::Exception& exc)
|
|
|
- {
|
|
|
- Application& app = Application::instance();
|
|
|
- app.logger().log(exc);
|
|
|
- delete this;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void onSocketShutdown(const AutoPtr<ShutdownNotification>& pNf)
|
|
|
- {
|
|
|
- delete this;
|
|
|
- }
|
|
|
-
|
|
|
+ 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<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onReadable));
|
|
|
+ _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ShutdownNotification>(*this, &EchoServiceHandler::onShutdown));
|
|
|
+ }
|
|
|
+
|
|
|
+ ~EchoServiceHandler()
|
|
|
+ {
|
|
|
+ // Application& app = Application::instance();
|
|
|
+ // app.logger().information("Disconnecting " + _socket.peerAddress().toString());
|
|
|
+
|
|
|
+ _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onReadable));
|
|
|
+ _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ShutdownNotification>(*this, &EchoServiceHandler::onShutdown));
|
|
|
+ delete [] _pBuffer;
|
|
|
+ }
|
|
|
+
|
|
|
+ void onReadable(const AutoPtr<ReadableNotification>& pNf)
|
|
|
+ {
|
|
|
+ int n = _socket.receiveBytes(_pBuffer, BUFFER_SIZE);
|
|
|
+ if (n > 0)
|
|
|
+ _socket.sendBytes(_pBuffer, n);
|
|
|
+ else
|
|
|
+ delete this;
|
|
|
+ }
|
|
|
+
|
|
|
+ void onShutdown(const AutoPtr<ShutdownNotification>& pNf)
|
|
|
+ {
|
|
|
+ delete this;
|
|
|
+ }
|
|
|
+
|
|
|
private:
|
|
|
- enum
|
|
|
- {
|
|
|
- BUFFER_SIZE = 1024
|
|
|
- };
|
|
|
-
|
|
|
- StreamSocket _socket;
|
|
|
- SocketReactor& _reactor;
|
|
|
- FIFOBuffer _fifoIn;
|
|
|
- FIFOBuffer _fifoOut;
|
|
|
+ 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()
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
- ~EchoServer()
|
|
|
- {
|
|
|
- }
|
|
|
+ EchoServer(): _helpRequested(false)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ ~EchoServer()
|
|
|
+ {
|
|
|
+ }
|
|
|
|
|
|
protected:
|
|
|
- void initialize(Application& self)
|
|
|
- {
|
|
|
- ServerApplication::initialize(self);
|
|
|
- }
|
|
|
-
|
|
|
- void uninitialize()
|
|
|
- {
|
|
|
- ServerApplication::uninitialize();
|
|
|
- }
|
|
|
-
|
|
|
- int main(const std::vector<std::string>& args)
|
|
|
- {
|
|
|
- if (args.size() < 1) {
|
|
|
- printf("Usage: cmd port\n");
|
|
|
- return -10;
|
|
|
+ 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<std::string>& 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<EchoServiceHandler> 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();
|
|
|
}
|
|
|
- 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<EchoServiceHandler> 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);
|
|
|
+ EchoServer app;
|
|
|
+ return app.run(argc, argv);
|
|
|
}
|