oatpp / example-websocket

Collection of oatpp WebSocket examples
https://oatpp.io/
Apache License 2.0
53 stars 17 forks source link

Multiple endpoint example? #5

Closed r2dliu closed 4 years ago

r2dliu commented 4 years ago

I have a working implementation of a normal (not async) websocket server. My use case is that I am sending lots of different kinds of data. some are short messages (a few bytes), and some are very long (several megabytes)

What I want to do is to be able to open multiple websocket connections from the client so that each connection can handle different message types, so there is no latency in waiting for a long message to finish before being able to send the next one. Otherwise, long messages can block short ones from coming through quickly.

What is the proper way to go about this? Do I need multiple connection handlers? Do I need a different ConnectionHandler for each endpoint? Do I need multiple WSListener classes or can I reuse the same one? I want each socket accessed from onAfterCreate to be different and have some way of being able to tell which endpoint each socket is associated with.

lganzzzo commented 4 years ago

Hello @r2dliu ,

Do I need a different ConnectionHandler for each endpoint?

Use one ConnectionHandler for all clients, unless you want to separate them for whatever reason. Use ConnectionHandler::ParameterMap to pass info of incoming peer to onAfterCreate method - like in this example

Do I need multiple WSListener classes or can I reuse the same one? I want each socket accessed from onAfterCreate to be different and have some way of being able to tell which endpoint each socket is associated with.

Create an instance of WSListener for each incoming peer - like in this example here


I'm giving links on async example - but in this case, the API is identical.

For a better understanding of how to manage peers, I would recommend taking a look at chat examples:


Other things to consider

In the case of WebSockets, you might want to consider Async API specifically for WS handlers.

You can have your API-Controller with handshake endpoints running in a simple mode while having WS handlers running in async - oatpp will automatically switch connection mode when delegating connection to WS connection handler.

In the chat-rooms example, you can see that amount of async code is minimal so it might not be so scary.

However, if you have to access databases from WebSockets handlers - then it's better to go with a simple API.


Please let me know if you have more questions.

Regards, Leonid

r2dliu commented 4 years ago

Thank for you the info!

One point I guess I should clarify is that I'm only expecting to have a single client, if that makes sense. I just need ultra low latency data transfer to this single browser client. So I don't have multiple peers.

In addition, what is the best pattern for actually accessing the websocket object itself? I understand that the intended pattern is probably to setup what I need in the onAfterCreate method and handle it there. But this is difficult for my pattern because I need access to the websocket from elsewhere, since as mentioned before I have lots of different data types but they are processed together in the same class and this processing is not tied to the existence of the websocket. It feels very wrong to const cast away the websocket and force the ability to do something janky like pass it around as a pointer for example. Let me know if you have better ideas.

lganzzzo commented 4 years ago

Hey,

One point I guess I should clarify is that I'm only expecting to have a single client, if that makes sense. I just need ultra low latency data transfer to this single browser client. So I don't have multiple peers.

Then yeah, you don't need any async processing.

In addition, what is the best pattern for actually accessing the websocket object itself? ...

Take a look at this simple server example.

There is nothing wrong with storing a pointer to your socket, just make sure you'll clean up all pointers in the onBeforeDestroy method.

Also, all socket callbacks have a socket passed as a parameter - so you don't actually need to store the socket (in most cases). And if you want to fire an independent event from the server - just store a pointer to the socket and ex.: ping it on the timer (again make sure that pointer will be cleared in the onBeforeDestroy)

                                /* Socket which receives the message */
                                                 |
                                                 |
                                                \|/
void WSListener::readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size)

Then you may associate an individual listener with each socket so you know for sure what type of message on which socket you handle.

Things to Remember

r2dliu commented 4 years ago

This is perfect, thank you

DingFeng9313 commented 2 years ago

I am facing a websocket crash problem, and I found this issue. As you mentioned "Sockets are not synchronized by default - so it's a good idea to have guards on all the socket writes."

I am broadcasting messages to all clients in WSInstanceListener class as in "chatter-room example". My implementation is as followed:

  1. I stored all clients in "onAfterCreate_NonBlocking" method as a map<int, std::shared_ptr>.
  2. Then I start a thread in Constructor of WSInstanceListener class, and broadcast messages to all Clients in this thread function.

How should I add lock_guard? Should I add lock_guard for every client operation? like create Client, erase Client, And Client->sendMessage("great oapp")?