oatpp / oatpp-websocket

oatpp-websocket submodule.
https://oatpp.io/
Apache License 2.0
83 stars 32 forks source link

unable to get the websocket instance of a websocket::connectionHandler object #44

Closed Saadharta closed 1 year ago

Saadharta commented 1 year ago

Hello there :)

I feel like i stumbled upon an edgy use case:

My server is primarily used for a REST API; however i also need to use the websocket mechanism so that my server has the ability to send to its clients that something happened, prompting the clients to interrogate the REST API for more details.

I don't need to send a lot; a high level access to WebSocket::Listener::onPing() might do the trick.

Apart rewriting my own sauce of the ConnectionProvider class (more specifically the Task subclass), is there any server-side ways to access opened websocket connections?

( It is more-or-less an issue related to https://github.com/oatpp/oatpp-websocket/issues/37 )

Kind regards,

Saadharta

Saadharta commented 1 year ago

Update: i followed loosely the AsyncRoomsServer example; i added a getter on the current instance of Lobby and added a getter on it's m_rooms map;

I also modified Lobby a little bit to make full use of the m_room map:

std::shared_ptr<Room> Lobby::getOrCreateRoom(const oatpp::String& roomName) {
  std::lock_guard<std::mutex> lock(m_roomsMutex);
  std::shared_ptr<Room>& room = m_rooms[roomName]; // may not be needed?
  if(!room) {
    room = std::make_shared<Room>(roomName);
    m_rooms[roomName] = room; // make sense to fill the Lobby's room map if there is a new room
  }
  return room;
}

void Lobby::onAfterCreate_NonBlocking(const std::shared_ptr<AsyncWebSocket>& socket, const std::shared_ptr<const ParameterMap>& params) {

  auto roomName = params->find("roomName")->second;
  auto nickname = params->find("nickname")->second;
  auto room = getOrCreateRoom(roomName);

  auto peer = std::make_shared<Peer>(socket, room, nickname, obtainNewUserId());
  socket->setListener(peer);

  m_room[roomName]->addPeer(peer); // using the map instead of the room instance
  m_room[roomName]->sendMessage(nickname + " joined " + roomName); //using the map instead of the room instance
}

after some tweakings, in my main mathod (i am not using the Server class for practical purposes)

// Run """server"""
std::shared_ptr<const std::unordered_map<oatpp::String, oatpp::String>> params;
oatpp::provider::ResourceHandle<oatpp::network::ConnectionHandler::IOStream> cH;

while (true) {
    cH = connectionProvider->get();
    if (cH.object) 
    {
        serverConnectionHandler->handleConnection(cH, params /* null params? */);
// give a bit of time for the request to be processed
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
// who is my client?
        std::cout
            << "Incoming connection from "
            << cH.object->getInputStreamContext().getProperties().get("peer_address")->c_str()
            << ":"
            << cH.object->getInputStreamContext().getProperties().get("peer_port")->c_str();
// who are my connections?
        for (auto wsR : Lobby::getLobbyInstance()->getRooms()) 
        {
            std::cout 
                << " on endpoint " 
                << wsR.first->c_str()
                << std::endl;
            wsR.second->sendMessage("I SEE YOU LITTLE CLIENT ON " + wsR.first->c_str() );
        }
    }
}

It is not perfect but it shows that it is possible for the server to send a message by itself to clients.

Saadharta