lw-support/src/tests/Net_EchoServerTCP.cpp.xxx

380 lines
8.9 KiB
Plaintext

/* lw-support/src/tests/Net_EchoServerTCP.cpp
*
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
* COPYING for more information / terms of license.
*/
#include "lw-support"
#include <iostream>
#include <sstream>
#include <list>
// Logging options
lw::Log Log(std::wcout, true), Err(std::wcerr, true);
int logRead, logWritten, logNew, logClosed;
//BEGIN // Buffer machinery ////////////////////////////////////////////
/// Maximum buffer size, in bytes.
int bufferSize = 0xFFFF;
/// Already-allocated buffers are stored here, for re-use.
std::list<lw::FIFO*> freeBuffers;
/// Allocate a buffer.
inline lw::FIFO* getBuffer()
{
if(freeBuffers.empty()) {
Log << lw::Log::start(L"Buffer")
<< L"Allocating a new buffer."
<< lw::Log::end;
return new lw::FIFO(bufferSize, bufferSize);
}
lw::FIFO* p = freeBuffers.front();
freeBuffers.pop_front();
return p;
}
/// Free a buffer.
inline void putBuffer(lw::FIFO* p)
{
freeBuffers.push_back(p);
}
//END // Buffer machinery //////////////////////////////////////////////
//BEGIN // Echo client /////////////////////////////////////////////////
class EchoClient : private lw::EventCallbackIO {
private:
std::wstring name;
lw::IODevice* client;
lw::FIFO* fifo;
bool eof;
// from EventCallbackIO
virtual bool ioReady(uint32_t flags);
public:
EchoClient(lw::EventManager& eventManager, lw::IODevice* client);
virtual ~EchoClient();
};
EchoClient::EchoClient(lw::EventManager& eventManager, lw::IODevice* client)
: client(client), fifo(getBuffer()), eof(false)
{
// build name for log
lw::NetClientTCP* tcp = dynamic_cast<lw::NetClientTCP*>(client);
if(tcp) {
std::wostringstream o;
o << tcp->remoteAddr().toString()
<< L", port "
<< tcp->remotePort();
name = o.str();
} else {
name = L"(unknown client type)";
}
// listen to all events
eventManager.registerDevice(client, this, ~0);
// log it
Log << lw::Log::start(name) << L"New connection." << lw::Log::end;
++logNew;
}
EchoClient::~EchoClient()
{
Log << lw::Log::start(name) << L"Deleting device." << lw::Log::end;
++logClosed;
putBuffer(fifo);
delete client;
}
bool EchoClient::ioReady(uint32_t flags)
{
bool ioRunning;
size_t amt;
// check for errors
if(flags & lw::IOEventError) {
try {
client->checkErrors();
}
catch(lw::Exception& e) {
Log << lw::Log::start(name)
<< L"Detected client error:\n\n"
<< e
<< lw::Log::end;
delete this;
return false;
}
Err << lw::Log::start(name)
<< L"epoll reports client error, but none detected."
<< lw::Log::end;
}
// check for HUP
if(flags & lw::IOEventHUP) {
Log << lw::Log::start(name)
<< L"HUP detected. Closing connection."
<< lw::Log::end;
delete this;
return false;
}
// check for urgent data
if(flags & lw::IOEventUrgent) {
Log << lw::Log::start(name)
<< L"Urgent data available. Rejecting connection."
<< lw::Log::end;
delete this;
return false;
}
// constantly try to drain the output buffer and fill the input buffer
do {
ioRunning = false;
// write
while(true) {
amt = fifo->getSize();
if(!amt) {
if(eof) {
Log << lw::Log::start(name)
<< L"Closing client connection."
<< lw::Log::end;
delete this;
return false;
}
break;
}
amt = client->write(fifo->getData(), amt);
if(!amt) break;
logWritten += amt;
ioRunning = true;
fifo->getData(amt);
}
// read
while(!eof) {
amt = fifo->getRemaining();
if(!amt) break;
try {
amt = client->read(fifo->getBuffer(amt), amt);
if(!amt) break;
logRead += amt;
fifo->addedData(amt);
}
catch(lw::EndOfFile&) {
eof = true;
}
ioRunning = true;
}
}while(ioRunning);
// done!
return true;
}
//END // Echo client ///////////////////////////////////////////////////
//BEGIN // Echo server /////////////////////////////////////////////////
class EchoServer : private lw::EventCallbackNetServer {
private:
lw::EventManager& eventManager;
lw::NetServerTCP netServer;
// from EventCallbackNetServer
virtual bool accept(lw::IODevice* client);
public:
EchoServer(lw::EventManager& eventManager,
uint16_t port, lw::NetAddressIPv4& addr);
virtual ~EchoServer()
{ }
};
EchoServer::EchoServer(lw::EventManager& eventManager,
uint16_t port, lw::NetAddressIPv4& addr)
: eventManager(eventManager)
{
netServer.listen(addr, port, 4);
netServer.registerCallback(eventManager, *this);
}
bool EchoServer::accept(lw::IODevice* client)
{
new EchoClient(eventManager, client);
return true;
}
//END // Echo server ///////////////////////////////////////////////////
//BEGIN // Main and helpers ////////////////////////////////////////////
void help();
void parseArguments(int argc, char** argv,
uint16_t& port,
int& bufferSize,
lw::NetAddressIPv4& netAddress
);
int main(int argc, char* argv[])
{
if(argc == 2 && !strcmp(argv[1], "--print-summary")) {
std::wcout << L"A TCP echo service (--help for more).\n";
return 0;
}
if(argc == 1 || (argc == 2 && !strcmp(argv[1], "--help"))) {
// empty argument list
help();
return 0;
}
try {
// parse command line arguments
uint16_t port = 0;
lw::NetAddressIPv4 netAddress;
parseArguments(argc, argv, port, bufferSize, netAddress);
// set up the event manager and server
lw::EventManager eventManager(5, 5);
EchoServer echoServer(eventManager, port, netAddress);
// event loop
while(true) {
logRead = 0;
logWritten = 0;
logNew = 0;
logClosed = 0;
eventManager.processEvents(-1);
Log << lw::Log::start(L"main()")
<< L"Bytes read this loop : " << logRead << L"\n"
L"Bytes written this loop : " << logWritten << L"\n"
L"New connections : " << logNew << L"\n"
L"Closed connections : " << logClosed
<< lw::Log::end;
}
}
catch(lw::Exception& e) {
Err << lw::Log::start(L"main()") << e << lw::Log::end;
}
catch(...) {
Err << lw::Log::start(L"main()")
<< L"Unknown exception caught."
<< lw::Log::end;
}
return 1;
}
void help()
{
std::wcout << L"Usage:\n"
L" Net_EchoServerTCP --port <port> --buffer <bufsize>\n"
L" --bind <ipaddr>\n"
L"\n"
L" <port> : the TCP port to listen on\n"
L"<bufsize> : the internal buffer size (default 64k)\n"
L" <ipaddr> : the IP address (dotted quad) to bind to (default any)\n"
L"\n";
}
void parseArguments(int argc, char** argv,
uint16_t& port,
int& bufferSize,
lw::NetAddressIPv4& netAddress)
{
bool portSeen = false;
for(int i = 1; i < argc; ++i) {
if(!strcmp(argv[i], "--port")) {
portSeen = true;
if(++i == argc)
throw lw::Exception(L"Expecting another argument after --port");
try {
port = lw::strToUInt16(lw::strToUcs4(argv[i]));
}
catch(lw::Exception& e) {
e.chain(L"parsing --port argument");
throw;
}
} else if(!strcmp(argv[i], "--buffer")) {
if(++i == argc)
throw lw::Exception(L"Expecting another argument after --buffer");
try {
bufferSize = lw::strToInt32(lw::strToUcs4(argv[i]));
}
catch(lw::Exception& e) {
e.chain(L"parsing --buffer argument");
throw;
}
} else if(!strcmp(argv[i], "--bind")) {
if(++i == argc)
throw lw::Exception(L"Expecting another argument after --bind");
try {
netAddress = lw::NetAddressIPv4::fromString(lw::strToUcs4(argv[i]));
}
catch(lw::Exception& e) {
e.chain(L"parsing --bind argument");
throw;
}
} else {
throw lw::Exception(L"Unknown parameter.");
}
}
}
//END // Main and helpers //////////////////////////////////////////////