1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
#include <cxxtools/arg.h>
#include <cxxtools/eventloop.h>
#include <cxxtools/log.h>
#include <cxxtools/net/tcpserver.h>
#include <cxxtools/net/tcpstream.h>
#include <algorithm>
#include <iostream>
#include <vector>
log_define("server")
class Responder;
////////////////////////////////////////////////////////////////////////
// Define a server class
//
class Server : public cxxtools::Connectable
{
cxxtools::net::TcpServer _server;
std::vector<Responder*> _responder;
void onNewConnection(cxxtools::net::TcpServer&);
void onClosed(cxxtools::net::TcpStream&);
public:
explicit Server(cxxtools::SelectorBase* selector, const std::string& ip, unsigned short port);
};
Server::Server(cxxtools::SelectorBase* selector, const std::string& ip, unsigned short port)
: _server(ip, port)
{
_server.setSelector(selector);
// When a connection arrives from a client the signal 'connectionPending' is emmitted.
// We connect that to our handler.
cxxtools::connect(_server.connectionPending, *this, &Server::onNewConnection);
}
////////////////////////////////////////////////////////////////////////
// The Responder class handles a single client.
//
class Responder : public cxxtools::net::TcpStream
{
void onInput(cxxtools::net::TcpStream&);
void onOutput(cxxtools::net::TcpStream&);
public:
explicit Responder(cxxtools::net::TcpServer& server);
};
////////////////////////////////////////////////////////////////////////
void Server::onNewConnection(cxxtools::net::TcpServer&)
{
// When a new connection arrives create a new client responder.
log_debug("new connection");
Responder* responder = new Responder(_server);
_responder.push_back(responder);
// When the client closes the connection the signal 'closed' is emmitted.
// Connect that to our handler onClosed.
cxxtools::connect(responder->closed, *this, &Server::onClosed);
}
void Server::onClosed(cxxtools::net::TcpStream& responder)
{
// When a client is closed we delete the Responder
std::vector<Responder*>::iterator it = std::find(_responder.begin(), _responder.end(), &responder);
if (it != _responder.end()) // should always be true
{
log_debug("closed");
_responder.erase(it);
delete *it;
}
else
{
log_warn("responder not found");
}
}
////////////////////////////////////////////////////////////////////////
Responder::Responder(cxxtools::net::TcpServer& server)
: cxxtools::net::TcpStream(server)
{
setSelector(server.selector());
// connect the signals 'inputReady' and 'outputReady' to our handlers.
cxxtools::connect(inputReady, *this, &Responder::onInput);
cxxtools::connect(outputReady, *this, &Responder::onOutput);
// Start waiting for input. When data arrives, the signal 'inputReady' is
// emmitted.
beginRead();
}
void Responder::onInput(cxxtools::net::TcpStream&)
{
log_debug("onInput");
// Data has arrived. End read reads data from the socket and fills our
// input buffer.
endRead();
if (in_avail() == 0)
{
// When 'inputReady' is emmitted without data the client has actually
// closed the connection. We emmit the signal 'closed'.
closed(*this);
return;
}
// echo input back to the client
char ch;
while (get(ch), *this)
{
// Put the character to the output buffer
put(ch);
}
// We wrote at least one character to the output buffer.
// We signal, that we want to send the data as soon as the socket is
// ready to accept outgoing data without blocking.
beginWrite();
// We called endRead. Now we want to continue reading and put the device
// back into read mode.
beginRead();
}
void Responder::onOutput(cxxtools::net::TcpStream&)
{
log_debug("out avail=" << buffer().out_avail());
// The device has signaled, that it is ready to accept data to be sent.
// The call to endWrite copies data from our output buffer to the socket.
endWrite();
// The socket may have accepted only part of our output buffer so there
// may be data left. We tell the device, that we want to transfer more
// data when there is space in the output buffer.
if (buffer().out_avail())
beginWrite();
}
////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
try
{
// read options from command line
// -l <ip> specifies the ip address of the interface to listen on
// a empty string tells cxxtools to listen on all interfaces.
// -p <port> specifies to port with a default value of 7000.
cxxtools::Arg<std::string> ip(argc, argv, 'l');
cxxtools::Arg<unsigned short> port(argc, argv, 'p', 7000);
// This initializes logging by reading a file 'log.xml' or
// 'log.properties' in the local directory. Optionally a file name or a
// object of type cxxtools::LogConfiguration could be passed to
// log_init().
log_init();
// We need a event loop, which handles I/O events
cxxtools::EventLoop loop;
// Our server runs in the loop.
Server server(&loop, ip, port);
// Run the loop
loop.run();
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
|