Domanda

Ho cercato di creare un modello TCP Server sulla base di eredità, con successo variabile. Questi server sono gestiti da un Singleton che ha il compito di chiudere questi server verso il basso e le altre funzioni di semplice manutenzione:

class TCPServer {
public:
    TCPServer();
    ~TCPServer();

    void Bind(TCPDaemon *daemon) {
        if(!daemon->IsRunning()) {
            throw TCPBindException("Daemon is inactive");
        }

        // if the port is not taken, bind this daemon to it
        if(this->servers.count(daemon->port())==0) {
            this->servers[daemon->port()]=daemon;
            ...
        } else {
            throw TCPBindException("Port is taken");
        }
    }

    void Shutdown() {
        MASON::UINT16 i;
        for(i=0;i<this->servers.size();i++) {
            this->Shutdown((*this->servers.begin()).first);
        }
    }

    void Shutdown(unsigned short port)  {
        if(this->servers.count(port)) {

            if(this->servers[port]->IsRunning()) {
                this->servers[port]->Stop();
            }

            delete this->servers[port];
            this->servers.erase(port);

        }
    }

private:
    std::map<unsigned short, TCPDaemon*> servers;

};

La funzione della classe di TCPDaemon Stop () è una pura virtuale. Il mio problema è che quando viene chiamata la funzione arresto (), si sta tentando di chiamare questo puro virtuale invece della versione derivata della classe. Come posso costringerlo a fare la cosa giusta?

Grazie in anticipo

[modifica] Ci dispiace non ho incluso il codice TCPDaemon prima, deriva da una classe TCPSocket (che ho controllato per essere al 100% che funziona ed è abbastanza auto-esplicativo). Eccolo:

class TCPDaemon: public TCPSocket {
public:
    TCPDaemon(unsigned short port) {
        this->_enabled=false;
        this->_host.ipaddr(INADDR_ANY);
        this->_host.port(port);
        this->paused=false;

        struct sockaddr_in opts=this->_host.Compile();

        #ifdef PLATFORM_WINDOWS
            WSADATA wsaData;
            if(WSAStartup(0x0202, &wsaData)) {
                throw TCPDaemonException("Failed to start WSA");
            }
        #endif

        this->raw_socket=socket(AF_INET, SOCK_STREAM, 0);
        if(this->raw_socket<=0) {
            throw TCPDaemonException("Failed to create socket");
        }

        if(int status=bind(this->raw_socket, (sockaddr*)&opts, sizeof(sockaddr))) {
            printf("error [%i]\r\n", status);
            throw TCPDaemonException("Failed to bind to port");
        }

        if(listen(this->raw_socket, 5)) {
            throw TCPDaemonException("Failed to listen on port");
        }

        this->_enabled=true;

    }

    virtual ~TCPDaemon() {
        this->Shutdown();
    }

    virtual void Start()=0;
    virtual void Run(TCPSocket*)=0;
    virtual void Stop()=0;

    unsigned short port() {
        return this->host().port();
    }

    bool IsRunning() {
        return this->_enabled;
    }

    TCPSocket *Accept() {
        SOCKET client;
        struct sockaddr client_addr;
        int len=sizeof(client_addr);
        client=accept(this->raw_socket, &client_addr, &len);

        return new TCPSocket(client, &client_addr);
    }

    void Shutdown() {

    }

private:
    bool _enabled;
    bool paused;

};

ed ecco un server di campione derivato e il suo metodo di creazione:

   class EchoServer: public TCPDaemon {
    public:
        EchoServer(MASON::UINT16 port): TCPDaemon(port) {
        }

        ~EchoServer() {}

        virtual void Start() {

        }

        virtual void Run(TCPSocket *client) {
            printf("RUN\r\n");
            Accessor<TCPSocket> acc_client=client;
            acc_client->Write(Accessor<Blob> (new Blob(std::string("hello!"))));
            acc_client->Disconnect();
        }

        virtual void Stop() {

        }

    };

myTCPServer->Bind(new EchoServer(8008));

[modifica + 1] Penso che quello che il problema si riduce a questo (ho potuto facilmente essere sbagliato): Ho uno std :: map della classe base, TCPDaemon, che ha una pura / funzione virtuale astratto Stop (). Sembra che quando chiamo Stop () attraverso una delle voci nella mappa, si sta tentando di chiamare TCPDaemon :: Stop (), in contrasto con la funzione di override EchoServer :: Stop (). Potrebbe essere questo il problema? Se sì, come posso risolverlo?

È stato utile?

Soluzione

Io non sono sicuro se questo è il problema che si sta vedendo, ma c'è sicuramente un problema nella seguente funzione.

void Shutdown() {
    MASON::UINT16 i;
    for(i=0;i<this->servers.size();i++) {
        this->Shutdown((*this->servers.begin()).first);
    }
}

Dopo ogni iteratore ciclo, i aumenterà di una AND size diminuirà, così avrete solo ShutDown la metà dei server. Forse mantenendo alcuni aperta è la causa di ulteriori errori.

Si potrebbe fare uno:

void Shutdown() {
    MASON::UINT16 i;
    std::size_t nrServers = this->servers.size();
    for(i=0;i<nrServers;i++) {
        this->Shutdown((*this->servers.begin()).first);
    }
}

o, che io preferirei come dimostra meglio l'intenzione del codice:

void Shutdown() {
    while (!this->servers.empty()) {
        this->Shutdown((*this->servers.begin()).first);
    }
}

Altri suggerimenti

Controlla la sintassi di ciò che si sta dichiarando:

class TCPDaemon
{
    virtual void stop() = 0;
};

class MyDaemon : public TCPDaemon
{
    virtual void stop()
    {
        //Do stuff here.
    }
};

Questo è il massimo che posso fare senza più codice.

EDIT:

Ok, quindi sembra che si sta utilizzando le funzioni astratte. Prossima domanda è questa: che cosa è l'errore hai trovato? Posso dire con certezza che si tratta di non di tentare di chiamare Stop () da TCPDeamon. Questo sarebbe impossibile, in quanto non è ancora implementata.

I finalmente lavorato fuori alla fine, grazie all'ingresso ho ricevuto. Il problema era con la chiamata di eliminazione nella TCPServer :: Shutdown (unsigned short), che stava causando una violazione di accesso di memoria in una parte completamente diversa del codice ... Un errore piuttosto noobie, sarò avvolgendo puntatori intelligenti intorno tutto al più presto.

Grazie per tutti i vostri commenti!

Se si dice che chiama il metodo virtuale pura invece di versione derivata di classe, che significa che la classe derivata chiama. Per es.

class t1
{
 public:
virtual ~t1(){};
virtual void foo()=0        
  {
    std::cout << "pure virtual";
};
};

class t2:public t1
{
public :

virtual void foo() 
    {
    t1::foo();
    std::cout << "derived class ";
};
};

Per quanto mi ricordo derivato unica classe oggetto può chiamare classe base funzione virtuale pura (sicuramente, se è implementato in classe base)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top