mercredi 27 décembre 2017

Detach thread with socket as class member

I have a game server implemented in Java, however, I am migrating to C++.

I am trying to implement a server to my game. I am facing a problem when I try to detach a thread in the Client class. This thread would be responsible to listen to the client socket while the client is connect. I tried different strategies, however, I would like to keep it optimized and simple as possible. I believe the problem is related to the form that I am declaring the socket.

For the answer, rather than just pointing the problem, would be nice to check one example that fits with my structure. Moreover, I am open to opinions to improve my structure and performance of the server.

In the main, after accepting a new connection I create a new Client, and move the socket to it.

using boost::asio::ip::tcp;

void server(boost::asio::io_service&, tcp::endpoint&);

int main(int argc, char* argv[]) {
    try {
        boost::asio::io_service io_service;
        tcp::endpoint end_point(tcp::v6(), 9990);
        server(io_service, end_point);
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    return 0;
}

inline void server(boost::asio::io_service& io_service, tcp::endpoint& end_point) {
    std::shared_ptr<NetworkManager> manager(new NetworkManager());
    tcp::acceptor acceptor(io_service, end_point);
    for (;;) {
        std::cout << "Waiting for new connection." << std::endl;
        acceptor.listen();
        tcp::socket sock(io_service);
        acceptor.accept(sock);
        std::cout << "New connection accepted." << std::endl;

        std::shared_ptr<Client> client = std::shared_ptr<Client>(new Client(std::move(sock), manager));
        manager->add_client(client);
    }
}

Client class

class Client {
private:
    std::string id;
    tcp::socket& sock;
    std::shared_ptr<InterfaceManager> manager;

    void session();
    void send_start();
    void update_transform(std::string);
    std::vector<unsigned char> int_to_bytes(int);
public:
    std::shared_ptr<Position> position;
    std::shared_ptr<Rotation> rotation;

    void send_to_client(std::string);

    // Constructor and deconstructor
    Client(tcp::socket, std::shared_ptr<InterfaceManager>);
    virtual ~Client();

    // Getters and Setters
    std::string get_id() const;
};

In the constructor of the Client, store the manager which will contains all clients for broacasting, define UUID, position, rotation (for the transform), and detach() the thread. I tried to join() and it works for one client (because it does not continues in the main after creating the Client object).

Client::Client(tcp::socket socket, std::shared_ptr<InterfaceManager> i_manager) :
        sock(socket) {
    // For broadcast
    this->manager = i_manager;

    // Create UUID
    boost::uuids::uuid uuid = boost::uuids::random_generator()();
    id = to_string(uuid);
    std::cout << "Client " << id << " connected." << std::endl;

    // Define position and rotation
    this->position = std::shared_ptr<Position>(new Position("0", "0", "0"));
    this->rotation = std::shared_ptr<Rotation>(new Rotation("0", "0", "0", "0"));

    // Start client thread
    std::thread(&Client::session, this).detach();
}

The thread Client::session is this coded as bellow. This is a simple receive/reply thread (just for testing).

void Client::session() {
    // Send start command
    send_start();
    try {
        for (;;) {
            char data[1024];

            boost::system::error_code error;
            sock.read_some(boost::asio::buffer(data), error);

            if (error == boost::asio::error::eof) {
                puts("Connection closed cleanly by peer");
                break;
            } else if (error) {
                puts("Some other error");
                throw boost::system::system_error(error);
            }

            std::string str(data);
            std::cout << "Data received: " << data << std::endl;

            //update_transform(str);
            //manager->broadcast(id, str);

            std::string s = "The received message is: " + str;

            char* answer = const_cast<char*>(s.c_str());
            size_t answer_length = std::strlen(answer);
            boost::asio::write(sock, boost::asio::buffer(answer, answer_length));

        }
    } catch (std::exception& e) {
        std::cerr << "Exception in thread: " << e.what() << "\n";
        sock.close();
    }
    std::cout << "Client " << id << " closed its connection." << std::endl;
    sock.close();
}

Aucun commentaire:

Enregistrer un commentaire