/* 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 #include #include // 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 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(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 --buffer \n" L" --bind \n" L"\n" L" : the TCP port to listen on\n" L" : the internal buffer size (default 64k)\n" L" : 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 //////////////////////////////////////////////