Fastest way to close a websocket and free up file discriptors

712 views Asked by At

I am using boost beast for making websocket connections, a process of mine may have a large number of streams/connections and while terminating the process I am calling blocking close of each websocket in destructor using :

                    if ( m_ws.next_layer().next_layer().is_open() )
                    {
                        boost::system::error_code ec;
                        m_ws.close(boost::beast::websocket::close_code::normal, ec);
                    }

Having a lot of websockets, makes terminating the process blocking for a long time, is there a way to force terminate(delete) a connection and free up underlying resources faster? Thanks in advance.

1

There are 1 answers

0
Jorge Omar Medra On

As I told you in the comments, closing the connection would be a fast operation on sockets but it doesn't take time and block the thread. In your case, I don't know how much work your program does to close each socket, but keep in mind that if your main thread ends, which means that the process has ended, the SO releases all the resources that it has been using, without closing each socket, I use this technic, and the WebSocket clients detect the end of the connections, but I close the socket when I have some problems with the protocol or the remote endpoint has been disconnected abruptly.

It could be useful that you share your code to see what other activities your code is doing.

By the way, let me share with you my code, where I have no problem to close websocket:

void wscnx::close(beast::websocket::close_code reason, const boost::system::error_code &ec)
{
    // std::cout << "\nwscnx::close\n";
    if (is_ssl && m_wss->is_open())
                     m_wss->close(beast::websocket::normal);
    else if (!is_ssl && m_ws->is_open())
        m_wss->close(beast::websocket::normal);
    
    if (!m_server.expired())
        m_server.lock()->cnx_closed(m_cnx_info, ec);
}

In my case, I'm using asynchronous methods to read and synchronous methods to write, I'm not using an asynchronous method to write to avoid the scenery of two simultaneous write operations.

Also, it's important to say that I'm using the asynchronous way to accept a new connection.

The code to accept the socket connection, where you can set the TIMES OUTS to write and read, instead of using timers.

void wscnx::accept_tcp()
{   
    m_ws->set_option(
        websocket::stream_base::timeout::suggested(
            beast::role_type::server));

    m_ws->set_option(websocket::stream_base::decorator(
        [](websocket::response_type &res)
        {
            res.set(http::field::server,
                    std::string(BOOST_BEAST_VERSION_STRING) +
                        " websocket-server-async");
        }));

    //std::cout << "wscnx::[" << m_id << "]:: TCP async_handshake\n";
    m_ws->async_accept(
        [self{shared_from_this()}](const boost::system::error_code &ec)
        {
            if (ec)
            {
                self->close(beast::websocket::close_code::protocol_error, ec);
                return;
            }
            // self->read_tcp();
            self->read();
        });
}

The code to read:

void wscnx::read()
{
    if (!is_ssl && !m_ws->is_open())
        return;
    else if (is_ssl  && !m_wss->is_open())
        return;

    auto f_read = [self{shared_from_this()}](const boost::system::error_code &ec, std::size_t bytes_transferred)
                 {
                    boost::ignore_unused(bytes_transferred);

                    // This indicates that the session was closed
                    if (ec == websocket::error::closed)
                    {
                        self->close(beast::websocket::close_code::normal, ec);
                        return;
                    }

                    if (ec)
                    {
                        self->close(beast::websocket::close_code::abnormal, ec);
                        return;
                    }

                    std::string data = beast::buffers_to_string(self->m_rx_buffer.data());
                    self->m_rx_buffer.consume(bytes_transferred);

                    if (!self->m_server.expired())
                    {
                        std::string_view vdata(data.c_str());
                        self->m_server.lock()->on_data_rx(self->m_cnx_info.id, vdata, self->cnx_info());
                    }
                    self->read(); 
                };//lambda

    if (!is_ssl)
        m_ws->async_read(m_rx_buffer, f_read);
    else
        m_wss->async_read(m_rx_buffer, f_read);
}

The code to write:

void wscnx::write(std::string_view data, bool close_on_write)
{
    std::unique_lock<std::mutex> u_lock(m_mtx_write);
    if ( (!is_ssl && !m_ws->is_open()) || (is_ssl && !m_wss->is_open()))
        return;

    boost::system::error_code ec;
    size_t bytes_transferred{0};
    if (is_ssl)
        bytes_transferred = m_wss->write(net::buffer(data), ec);
    else
        bytes_transferred = m_ws->write(net::buffer(data), ec);
    
    boost::ignore_unused(bytes_transferred);

    // This indicates that the session was closed
    if (ec == websocket::error::closed)
    {
        // std::cout << "[wscnx::[" << self->m_id << "]::on wirite] Error: " << ec.message() << "\n";
        close(beast::websocket::close_code::normal, ec);
        return;
    }

    if (ec)
    {
        // std::cout << "[wscnx::[" << self->m_id << "]::on wirite] Error READING: " << ec.message() << "\n";
        close(beast::websocket::close_code::abnormal, ec);
        return;
    }

    if (close_on_write)
        close(beast::websocket::close_code::normal, ec);
}

If you want to see the whole code, this is the Link. The project is still in the developer phase, but it works.