Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Poco::Net Server & Client TCP Connection Event Handler

I am starting a new project and at the same time have just discovered Poco Library, which I find absolutely amazing. However I am a bit lost, as examples are not plenty.

I have a ServerApplication->TCPServer->ServerSocket + TCPServerConnectionFactory->TCPServerconnection approach as indicated by the examples. I am inheriting from the PocoNet classes as instructed. Right now I can run my server as a service, & receive incoming connections.

I want to take an event handling approach to the following: on a per-connection (or per client) basis, handle events such as data available to read on the client socket, error occurred on the client socket (disconnected or timeout), send data without error on client socket.

How do I go about doing it? Is Poco/Foundation/Events what I am looking for, or is there some mechanism implemented in Poco::Net?

I have seen the Poco::Net::NetExpections but they do not appear to be thrown in my TCPServerConnection-derived class when a netcat connection closes.

like image 810
Ælex Avatar asked Jan 31 '13 18:01

Ælex


2 Answers

What I ended up using is a different approach as TCPServer is a different beast altogether. Following the example posted here I ended up with a class inheriting from ServerApplication, and a class which becomes essentially the connection handler by a SocketReactor.

Deamonizer header:

class Daemon : public ServerApplication
{
  public:
    Daemon();
    /// @Brief The main loop of the daemon, everything must take place here
    int main();
};

Deamonizer implementation:

int Daemon::main()
{
  // Server Socket
  ServerSocket svs(2222);
  // Reactor-Notifier
  SocketReactor reactor;
  Poco::Timespan timeout(2000000); // 2Sec
  reactor.setTimeout(timeout);
  // Server-Acceptor
  SocketAcceptor<ConnectionHandler> acceptor(svs, reactor);
  // Threaded Reactor
  Thread thread;
  thread.start(reactor);
  // Wait for CTRL+C
  waitForTerminationRequest();
  // Stop Reactor
  reactor.stop();
  thread.join();
  return Application::EXIT_OK;  
}

The handler class can be anything as long as it has a conforming Constructor (see Poco::Net documentation for this). In my case the header looks like this:

class ConnectionHandler
{
  public:

    /**
     * @Brief Constructor of the Connection Handler
     * @Note Each object is unique to an accepted connection
     * @Param SteamSocket is the socket accepting the connections
     * @See SocketAcceptor http://pocoproject.org/docs/Poco.Net.SocketAcceptor.html
     * @Param SocketReactor is the reacting engine (threaded) which creates notifications about the socket
     */
    ConnectionHandler(StreamSocket &, SocketReactor &);

    /**
     * @Brief Destructor
     */
    ~ConnectionHandler();

    /**
    * @Brief Event Handler when Socket becomes Readable, i.e: there is data waiting to be read
    */
    void onSocketReadable(const AutoPtr<ReadableNotification>& pNf);

    /**
    * @Brief Event Handler when Socket was written, i.e: confirmation of data sent away (not received by client)
    */
    void onSocketWritable(const AutoPtr<WritableNotification>& pNf);

    /**
    * @Brief Event Handler when Socket was shutdown on the remote/peer side
    */
    void onSocketShutdown(const AutoPtr<ShutdownNotification>& pNf);

    /**
    * @Brief Event Handler when Socket throws an error
    */
    void onSocketError(const AutoPtr<ErrorNotification>& pNf);

    /**
    * @Brief Event Handler when Socket times-out
    */
    void onSocketTimeout(const AutoPtr<TimeoutNotification>& pNf);

  private:

    /**
     * @Brief Read bytes from the socket, depending on available bytes on socket
     */
    void readBytes();

    /**
     * @Brief Send message to the socket
     * @Param std::string is the message (null terminated)
     */
    void sendMessage(std::string);

    /// Stream Socket
    StreamSocket _socket;

    /// Socket Reactor-Notifier
    SocketReactor& _reactor;

    /// Received Data Buffer
    std::vector<char *> in_buffer;
};

How you implement the handler is up to you, provided the only thing that you need to do is register the class methods which handle events like so:

  _reactor.addEventHandler(_socket,NObserver<ConnectionHandler, ReadableNotification>(*this, &ConnectionHandler::onSocketReadable));
  _reactor.addEventHandler(_socket,NObserver<ConnectionHandler, ShutdownNotification>(*this, &ConnectionHandler::onSocketShutdown));
  _reactor.addEventHandler(_socket,NObserver<ConnectionHandler, ErrorNotification>(*this, &ConnectionHandler::onSocketError));
  _reactor.addEventHandler(_socket,NObserver<ConnectionHandler, TimeoutNotification>(*this, &ConnectionHandler::onSocketTimeout));

All in all, two classes, a few lines of code, simple and clean. Absolutely starting to love Poco library! :)

like image 88
Ælex Avatar answered Oct 14 '22 11:10

Ælex


Try with this:

#include <iostream>
#include "Poco/Net/TCPServer.h"
#include "Poco/Net/TCPServerParams.h"
#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/Socket.h"
using namespace std;

class newConnection: public Poco::Net::TCPServerConnection {
public:
    newConnection(const Poco::Net::StreamSocket& s) :
        Poco::Net::TCPServerConnection(s) {
    }

    void run() {
        cout << "New connection from: " << socket().peerAddress().host().toString() <<  endl << flush;
        bool isOpen = true;
        Poco::Timespan timeOut(10,0);
        unsigned char incommingBuffer[1000];
        while(isOpen){
            if (socket().poll(timeOut,Poco::Net::Socket::SELECT_READ) == false){
                cout << "TIMEOUT!" << endl << flush;
            }
            else{
                cout << "RX EVENT!!! ---> "   << flush;
                int nBytes = -1;

                try {
                    nBytes = socket().receiveBytes(incommingBuffer, sizeof(incommingBuffer));
                }
                catch (Poco::Exception& exc) {
                    //Handle your network errors.
                    cerr << "Network error: " << exc.displayText() << endl;
                    isOpen = false;
                }


                if (nBytes==0){
                    cout << "Client closes connection!" << endl << flush;
                    isOpen = false;
                }
                else{
                    cout << "Receiving nBytes: " << nBytes << endl << flush;
                }
            }
        }
        cout << "Connection finished!" << endl << flush;
    }
};

int main(int argc, char** argv) {

    //Create a server socket to listen.
    Poco::Net::ServerSocket svs(1234);

    //Configure some server params.
    Poco::Net::TCPServerParams* pParams = new Poco::Net::TCPServerParams();
    pParams->setMaxThreads(4);
    pParams->setMaxQueued(4);
    pParams->setThreadIdleTime(100);

    //Create your server
    Poco::Net::TCPServer myServer(new Poco::Net::TCPServerConnectionFactoryImpl<newConnection>(), svs, pParams);
    myServer.start();

    while(1);

    return 0;
}
like image 32
Cesar Ortiz Avatar answered Oct 14 '22 11:10

Cesar Ortiz