drogon icon indicating copy to clipboard operation
drogon copied to clipboard

WebSocketConnectionPtr send doesn't works all the times

Open TheEnigmist opened this issue 1 year ago • 2 comments

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 avatar Oct 06 '23 21:10 TheEnigmist

@TheEnigmist Thanks for your feedback. Have you tried the following example? https://github.com/drogonframework/drogon/tree/master/examples/websocket_server

an-tao avatar Oct 07 '23 01:10 an-tao

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

TheEnigmist avatar Oct 07 '23 22:10 TheEnigmist