dimanche 16 juin 2019

Dependency Injection and Threads : Segmentation fault

I have the following classes and functions:

#ifndef HTTP_NETWORK
#define HTTP_NETWORK

#include <string>
#include <arpa/inet.h>

//Dummy Value to be changed
#define MAXPENDING 5

class Exception {
    public:
    Exception(std::string message):message(message){}
    std::string getMessage();
    private:
    std::string message;
};

class NetworkException:public Exception {
    public:
    NetworkException(std::string message) : Exception(message) {}
};

//A generic way to handle Network Connections
class ConnectionHandler {
    public:
    /**
    * @return 0 Close Connection 1 do not close
    */
    virtual int handle(int socketid) = 0;
};

/**
 * Because the only job s to call the handle method of ConnectionHandler,
 * I assume there's no need for a class.
 * 
 * Is is used to call the handler in a std::thread.
 */
int callHandler(ConnectionHandler *c, int sockerId);        

class TCPServer {
    public:
        TCPServer(int port, std::string address, ConnectionHandler *c);
        ~TCPServer();
        void listen();
    private:
        int port;
        //Socket file Descriptor
        int servSock;
        struct sockaddr_in ServAddr;
        ConnectionHandler *c = NULL;
};


#endif

And are implemented like that:

#include"network.h"

#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <iostream>
#include <cstring>
#include <string>
#include <thread>

std::string Exception::getMessage(){
    return this->message;
}

TCPServer::TCPServer(int port,std::string address, ConnectionHandler *c)
:port(port){

    if ((this->servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        throw NetworkException(std::string("SOCKET Error: could not create basic socket"));
    }

    memset(&this->ServAddr,0,sizeof(this->ServAddr));

    ServAddr.sin_family = AF_INET;
    ServAddr.sin_addr.s_addr = inet_addr(address.c_str());
    ServAddr.sin_port = htons(port);


    if (bind(this->servSock, (struct sockaddr *) &this->ServAddr, sizeof(this->ServAddr)) < 0) {
        throw NetworkException(std::string("SOCKET Error: Failed to bind a socket"));
    }

    if (::listen(this->servSock, MAXPENDING) < 0) {
        throw NetworkException(std::string("SOCKET Error: Failed to Listen"));
    }

    if(c==NULL){
       throw Exception("You should provice a connection Handler"); 
    }

    this->c=c;
}

void TCPServer::listen(){

    struct sockaddr_in ClntAddr;     /* Client address */
    socklen_t clntLen = (socklen_t)sizeof(ClntAddr);
    int clntSock;                    /* Socket descriptor for client */

    for (;;) {
       if ((clntSock = accept(servSock, (struct sockaddr *) &ClntAddr, &clntLen)) < 0) {
           std::cout<<"Failed to fetch"<<std::endl;
       }

       std::cout << "Handling client: " << inet_ntoa(ClntAddr.sin_addr) << std::endl;
       send(clntSock, "WELCOME", 6, 0);

       std::thread handleConnectionThread(callHandler, this->c, clntSock);
    //    this->c->handle(clntSock);
       close(clntSock);
    }
}

TCPServer::~TCPServer(){
 close(this->servSock);
}

int callHandler(ConnectionHandler *c, int socketId){
    return c->handle(socketId);
}

But I get a segmentation fault at:

int callHandler(ConnectionHandler *c, int socketId){
    return c->handle(socketId);
}

As Gdb shows:

(gdb) info args
c = 0x7fffffffdc30
socketId = 4
(gdb) continue
Continuing.
terminate called without an active exception
Command Sent: 
[Thread 0x7ffff6e85700 (LWP 21845) exited]

Thread 1 "server" received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffff7fd0740 (LWP 21840)]
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51  ../sysdeps/unix/sysv/linux/raise.c: Δεν υπάρχει τέτοιο αρχείο ή κατάλογος.
(gdb) 

So I thought to use an std::shared_ptr for class assignment:

Therefore the TCPServer is now defined as:

class TCPServer {
    public:
        TCPServer(int port, std::string address, std::shared_ptr<ConnectionHandler> c);
        ~TCPServer();
        void listen();
    private:
        int port;
        //Socket file Descriptor
        int servSock;
        struct sockaddr_in ServAddr;
        std::shared_ptr<ConnectionHandler> c;
};

And the callHandlerFunction functions is defined like that:

int callHandler(std::shared_ptr<ConnectionHandler> c, int sockerId);        

On the constructor of the TCPServer class I initialize the shared pointer like that:


// On header file
class TCPServer {
    public:
        TCPServer(int port, std::string address, std::shared_ptr<ConnectionHandler> c);
        ~TCPServer();
        void listen();
    private:
        int port;
        //Socket file Descriptor
        int servSock;
        struct sockaddr_in ServAddr;
        std::shared_ptr<ConnectionHandler> c;
};


//Constructor Implementation
TCPServer::TCPServer(int port,std::string address, std::shared_ptr<ConnectionHandler> c):port(port),c(c){

    if ((this->servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        throw NetworkException(std::string("SOCKET Error: could not create basic socket"));
    }

    memset(&this->ServAddr,0,sizeof(this->ServAddr));

    ServAddr.sin_family = AF_INET;
    ServAddr.sin_addr.s_addr = inet_addr(address.c_str());
    ServAddr.sin_port = htons(port);


    if (bind(this->servSock, (struct sockaddr *) &this->ServAddr, sizeof(this->ServAddr)) < 0) {
        throw NetworkException(std::string("SOCKET Error: Failed to bind a socket"));
    }

    if (::listen(this->servSock, MAXPENDING) < 0) {
        throw NetworkException(std::string("SOCKET Error: Failed to Listen"));
    }
}

Whilst the function callHandler is defined and implemented like that:

int callHandler(std::shared_ptr<ConnectionHandler> c, int sockerId);        

int callHandler(std::shared_ptr<ConnectionHandler> c, int socketId){
    return c->handle(socketId);
}

A sample code of TCP server initialization is:

#include<iostream>
#include<string>
#include<memory>

#include "socket/network.h"

class SimpleCommandHandler:: public ConnectionHandler{
   public:
   int handle(int socketid){
      // Handle connection here
   }  
}
int main(){
    try {
        std::shared_ptr<ConnectionHandler> c (new SimpleCommandHandler());
        TCPServer server(7070, "127.0.0.1", c);
        server.listen();
    } catch(Exception e) {
        std::cout<< e.getMessage();
        return -1;
    }
    return 0;
}

But on gdb I get the following error:

Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Handling client: 127.0.0.1
[New Thread 0x7ffff6e85700 (LWP 23889)]
terminate called without an active exception

Thread 1 "server" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51  ../sysdeps/unix/sysv/linux/raise.c: Δεν υπάρχει τέτοιο αρχείο ή κατάλογος.

SO do you know why I get the ABORT error?

Aucun commentaire:

Enregistrer un commentaire