drogon
drogon copied to clipboard
WebSocketConnectionPtr send doesn't works all the times
I'm trying to send message to a specific WebSocketConnectionPtr after an HTTP request, but the message is not always really sent.
- WebSocketConnectionPtr->connected() is true;
- WebSocketController correctly receive message from that connection;
- I checked WebSocketConnectionPtr address and are the same between calls;
This is how I manage a bridge between HttpController and WebSocketController:
class WSBridge{
public:
static WSBridge& getInstance(){
static PubSubManager instance;
return instance;
}
~WSBridge();
void addConnection(std::string id, drogon::WebSocketConnectionPtr wsConnection){
connections[id].push_back(wsConnection);
}
void removeConnection(std::string id, drogon::WebSocketConnectionPtr wsConnection){
auto conns = connections[id];
conns.erase(std::remove(conns.begin(), conns.end(), wsConnection), conns.end());
if (conns.size() == 0) {
connections.erase(broadcaster_id);
} else {
connections[id] = conns;
}
}
void broadcast(std::string id, std::string message){
if (connections.find(id) != connections.end()) {
for (auto& connection : connections[id]) {
connection->send(message);
}
}
}
private:
explicit WSBridge();
std::unordered_map<std::string, std::vector<drogon::WebSocketConnectionPtr>> connections;
};
I've first used drogon::PubSubService and the behaviour is the same, so I moved to unordered_map, maybe I rewrite PubSubService logics.
Adding connection in handleNewConnection
with PubSubManager::getInstance().addConnection(id, wsConnPtr);
Sending message with PubSubManager::getInstance().broadcast(id, "A MSG");
Expected behavior Receiving always messages on broadcast calls
Additional context Sometimes I need to restart drogon once and reconnect the WS clients to correctly receive message, sometimes even after 2 or 3 tries it doens't work. I still can't reproduce it 100%, it is still a random behaviour.
It seems like the bugged WebSocketConnectionPtr is not properly connected, indeed when this behaviour occurs and shutdown drogon the WS is still "connected" and is not true, the one it works properly disconnect as soon I shutdown drogon
Tesing on Windows 10 with Postman client and drogon server on windows too
@TheEnigmist Thanks for your feedback. Have you tried the following example? https://github.com/drogonframework/drogon/tree/master/examples/websocket_server
Yes I tested with this example and this behaviour appears. I made some edit, thinking that the problem could be the static singleton class I moved everything in the WebSocketController and followed the example using a PubSubService. Than I added HttpController as subclass other than WebSocketController, so I can send message on a Post message. Again if a client connects to the websocket sometimes it works liek a charm and sometimes it doesn't receive messages and trying to disconnecting it stuck in a "Disconnecting" state. This is a base sample test class: `struct Subscriber { std::string id; drogon::SubscriberID sub_id; };
class TestClass: public WebSocketController<TestClass>, public HttpController<TestClass> {
public:
WS_PATH_LIST_BEGIN
WS_PATH_ADD("/", Get,);
ADD_METHOD_TO(TestClass::sendMsg, "/{1}", Post);
WS_PATH_LIST_END
void handleNewMessage(const WebSocketConnectionPtr&, std::string&&, const WebSocketMessageType&) override{
auto& s = wsConnPtr->getContextRef<Subscriber>();
if (type == WebSocketMessageType::Text) {
LOG_INFO << "WS " << s.id << " says: " << message;
}
}
void handleNewConnection(const HttpRequestPtr&, const WebSocketConnectionPtr&) override {
auto id = req->getParameter("id");
Subscriber s;
s.id = id;
s.sub_id = connections.subscribe(id, [wsConnPtr](const std::string& topic, const std::string& message) {
(void)topic;
wsConnPtr->send(message);
});
wsConnPtr->setContext(std::make_shared<Subscriber>(std::move(s)));
}
void handleConnectionClosed(const WebSocketConnectionPtr&) override{
auto& s = wsConnPtr->getContextRef<Subscriber>();
connections.unsubscribe(s.id, s.sub_id);
}
void sendMsg(const HttpRequestPtr& req,std::function<void(const HttpResponsePtr&)>&& callback,int64_t id){
connections.publish(std::to_string(id), "TEST");
Json::Value ret;
HttpResponsePtr resp;
ret["message"] = "OK";
resp = HttpResponse::newHttpJsonResponse(ret);
resp->setStatusCode(k200OK);
callback(resp);
}
private:
PubSubService<std::string> connections;
};
Hope you can replicate this error