380 lines
8.9 KiB
Plaintext
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 //////////////////////////////////////////////
|