1//
2// EchoServer.cpp
3//
4// This sample demonstrates the SocketReactor and SocketAcceptor classes.
5//
6// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
7// and Contributors.
8//
9// SPDX-License-Identifier: BSL-1.0
10//
11
12
13#include "Poco/Net/SocketReactor.h"
14#include "Poco/Net/SocketAcceptor.h"
15#include "Poco/Net/SocketNotification.h"
16#include "Poco/Net/StreamSocket.h"
17#include "Poco/Net/ServerSocket.h"
18#include "Poco/NObserver.h"
19#include "Poco/Exception.h"
20#include "Poco/Thread.h"
21#include "Poco/FIFOBuffer.h"
22#include "Poco/Delegate.h"
23#include "Poco/Util/ServerApplication.h"
24#include "Poco/Util/Option.h"
25#include "Poco/Util/OptionSet.h"
26#include "Poco/Util/HelpFormatter.h"
27#include <iostream>
28
29
30using Poco::Net::SocketReactor;
31using Poco::Net::SocketAcceptor;
32using Poco::Net::ReadableNotification;
33using Poco::Net::WritableNotification;
34using Poco::Net::ShutdownNotification;
35using Poco::Net::ServerSocket;
36using Poco::Net::StreamSocket;
37using Poco::NObserver;
38using Poco::AutoPtr;
39using Poco::Thread;
40using Poco::FIFOBuffer;
41using Poco::delegate;
42using Poco::Util::ServerApplication;
43using Poco::Util::Application;
44using Poco::Util::Option;
45using Poco::Util::OptionSet;
46using Poco::Util::HelpFormatter;
47
48
49class EchoServiceHandler
50 /// I/O handler class. This class (un)registers handlers for I/O based on
51 /// data availability. To ensure non-blocking behavior and alleviate spurious
52 /// socket writability callback triggering when no data to be sent is available,
53 /// FIFO buffers are used. I/O FIFOBuffer sends notifications on transitions
54 /// from [1] non-readable (i.e. empty) to readable, [2] writable to non-writable
55 /// (i.e. full) and [3] non-writable (i.e. full) to writable.
56 /// Based on these notifications, the handler member functions react by
57 /// enabling/disabling respective reactor framework notifications.
58{
59public:
60 EchoServiceHandler(StreamSocket& socket, SocketReactor& reactor):
61 _socket(socket),
62 _reactor(reactor),
63 _fifoIn(BUFFER_SIZE, true),
64 _fifoOut(BUFFER_SIZE, true)
65 {
66 Application& app = Application::instance();
67 app.logger().information("Connection from " + socket.peerAddress().toString());
68
69 _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
70 _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ShutdownNotification>(*this, &EchoServiceHandler::onSocketShutdown));
71
72 _fifoOut.readable += delegate(this, &EchoServiceHandler::onFIFOOutReadable);
73 _fifoIn.writable += delegate(this, &EchoServiceHandler::onFIFOInWritable);
74 }
75
76 ~EchoServiceHandler()
77 {
78 Application& app = Application::instance();
79 try
80 {
81 app.logger().information("Disconnecting " + _socket.peerAddress().toString());
82 }
83 catch (...)
84 {
85 }
86 _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
87 _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, WritableNotification>(*this, &EchoServiceHandler::onSocketWritable));
88 _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ShutdownNotification>(*this, &EchoServiceHandler::onSocketShutdown));
89
90 _fifoOut.readable -= delegate(this, &EchoServiceHandler::onFIFOOutReadable);
91 _fifoIn.writable -= delegate(this, &EchoServiceHandler::onFIFOInWritable);
92 }
93
94 void onFIFOOutReadable(bool& b)
95 {
96 if (b)
97 _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, WritableNotification>(*this, &EchoServiceHandler::onSocketWritable));
98 else
99 _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, WritableNotification>(*this, &EchoServiceHandler::onSocketWritable));
100 }
101
102 void onFIFOInWritable(bool& b)
103 {
104 if (b)
105 _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
106 else
107 _reactor.removeEventHandler(_socket, NObserver<EchoServiceHandler, ReadableNotification>(*this, &EchoServiceHandler::onSocketReadable));
108 }
109
110 void onSocketReadable(const AutoPtr<ReadableNotification>& pNf)
111 {
112 try
113 {
114 int len = _socket.receiveBytes(_fifoIn);
115 if (len > 0)
116 {
117 _fifoIn.drain(_fifoOut.write(_fifoIn.buffer(), _fifoIn.used()));
118 }
119 else
120 {
121 delete this;
122 }
123 }
124 catch (Poco::Exception& exc)
125 {
126 Application& app = Application::instance();
127 app.logger().log(exc);
128 delete this;
129 }
130 }
131
132 void onSocketWritable(const AutoPtr<WritableNotification>& pNf)
133 {
134 try
135 {
136 _socket.sendBytes(_fifoOut);
137 }
138 catch (Poco::Exception& exc)
139 {
140 Application& app = Application::instance();
141 app.logger().log(exc);
142 delete this;
143 }
144 }
145
146 void onSocketShutdown(const AutoPtr<ShutdownNotification>& pNf)
147 {
148 delete this;
149 }
150
151private:
152 enum
153 {
154 BUFFER_SIZE = 1024
155 };
156
157 StreamSocket _socket;
158 SocketReactor& _reactor;
159 FIFOBuffer _fifoIn;
160 FIFOBuffer _fifoOut;
161};
162
163
164class EchoServer: public Poco::Util::ServerApplication
165 /// The main application class.
166 ///
167 /// This class handles command-line arguments and
168 /// configuration files.
169 /// Start the EchoServer executable with the help
170 /// option (/help on Windows, --help on Unix) for
171 /// the available command line options.
172 ///
173 /// To use the sample configuration file (EchoServer.properties),
174 /// copy the file to the directory where the EchoServer executable
175 /// resides. If you start the debug version of the EchoServer
176 /// (EchoServerd[.exe]), you must also create a copy of the configuration
177 /// file named EchoServerd.properties. In the configuration file, you
178 /// can specify the port on which the server is listening (default
179 /// 9977) and the format of the date/time string sent back to the client.
180 ///
181 /// To test the EchoServer you can use any telnet client (telnet localhost 9977).
182{
183public:
184 EchoServer(): _helpRequested(false)
185 {
186 }
187
188 ~EchoServer()
189 {
190 }
191
192protected:
193 void initialize(Application& self)
194 {
195 loadConfiguration(); // load default configuration files, if present
196 ServerApplication::initialize(self);
197 }
198
199 void uninitialize()
200 {
201 ServerApplication::uninitialize();
202 }
203
204 void defineOptions(OptionSet& options)
205 {
206 ServerApplication::defineOptions(options);
207
208 options.addOption(
209 Option("help", "h", "display help information on command line arguments")
210 .required(false)
211 .repeatable(false));
212 }
213
214 void handleOption(const std::string& name, const std::string& value)
215 {
216 ServerApplication::handleOption(name, value);
217
218 if (name == "help")
219 _helpRequested = true;
220 }
221
222 void displayHelp()
223 {
224 HelpFormatter helpFormatter(options());
225 helpFormatter.setCommand(commandName());
226 helpFormatter.setUsage("OPTIONS");
227 helpFormatter.setHeader("An echo server implemented using the Reactor and Acceptor patterns.");
228 helpFormatter.format(std::cout);
229 }
230
231 int main(const std::vector<std::string>& args)
232 {
233 if (_helpRequested)
234 {
235 displayHelp();
236 }
237 else
238 {
239 // get parameters from configuration file
240 unsigned short port = (unsigned short) config().getInt("EchoServer.port", 9977);
241
242 // set-up a server socket
243 ServerSocket svs(port);
244 // set-up a SocketReactor...
245 SocketReactor reactor;
246 // ... and a SocketAcceptor
247 SocketAcceptor<EchoServiceHandler> acceptor(svs, reactor);
248 // run the reactor in its own thread so that we can wait for
249 // a termination request
250 Thread thread;
251 thread.start(reactor);
252 // wait for CTRL-C or kill
253 waitForTerminationRequest();
254 // Stop the SocketReactor
255 reactor.stop();
256 thread.join();
257 }
258 return Application::EXIT_OK;
259 }
260
261private:
262 bool _helpRequested;
263};
264
265
266int main(int argc, char** argv)
267{
268 EchoServer app;
269 return app.run(argc, argv);
270}
271