WebSocket usage in C++
-
Hi, i've spent quite some time now, setting up a websocket server in my plugin. The documentation unfortunately doesn't have many usage examples of the network.framework. Here is my current code that should setup and start the websocket correctly:
The instance of this class is global in the plugin main file and gets deleted when thePluginEnd
function is called. The problem that I currently have is this line:
mWebSocketServer.ObservableConnected(true).AddObserver( [](const maxon::NetworkWebSocketConnectionRef& webSocket, const maxon::DataDictionary& request) -> maxon::Result<void> { return maxon::OK; }, nullptr);
This code does not compile, as the lambda can not be converted to maxon::ObserverObjectInterface*. But the docs here: https://developers.maxon.net/docs/cpp/2025_1_0/page_maxonapi_observers_usage.html state that Observers can take in lambdas and according to the header file the parameters of the lambda should be correct?
About some hints into the right direction I would be very happy
-
Hi @Cankar001 we do not really have any official example for the Websocket, but fortunately I did make the Visual Studio Code Bridge project, which have the duty to exchange code from VSCode to Cinema via Websocket, and it's open source. So it is not nicely documented and explained as an example could be but it can serve you as a base.
So find this example in Cinema-4D-Legacy-Visual-Studio-Code-Bridge-Plugin and look at the websocket_json_codeexchange.h and cpp.The main entry point is in WebSocketJsonCodeExchangeImpl::Start where all the observable are properly feed.
Three special notes:
- The code was written for S26 and is now directly merged into Cinema 4D, so it is not up to date, so for the observable the new way is to call it like that:
g_wsServer.ObservableHandshake(true).AddObserver(OnHandShake) iferr_return; g_wsServer.ObservableConnected(true).AddObserver(OnConnected) iferr_return; g_wsServer.ObservableDisconnected(true).AddObserver(OnDisconnected) iferr_return; g_wsServer.ObservableMessage(true).AddObserver(OnMessage) iferr_return;
- ObservableHandshake observable always need to be defined otherwise you won't be able to connect. Even if it's empty (like mine in OnHandShake).
- You need to close all connections before calling StopWebServer otherwise it will wait forever. This means you needs to keep tracks of all theses connections somehow. In my case I do it by storing connections in a BaseArray during the OnConnect within the WebSocketJsonCodeExchangeImpl which is as a Singleton (so only one instance of it exist).
Regarding your question it does not compile because you are calling it wrongly if I may say. First you pass a nullptr argument at the end that is not needed and secondly you do not handle the error that AddObserver emit which is probably the cause of the non-compilation. I would advice you to read Maxon API - Error Handling. With that's said here the adapted version that compile:
g_wsServer.ObservableConnected(true).AddObserver( [](const maxon::NetworkWebSocketConnectionRef& webSocket, const maxon::DataDictionary& request) -> maxon::Result<void> { iferr_scope; return maxon::OK; }) iferr_return;
If you need more help and there is detail you have hard time to understand in the code example do not hesitate to ask questions.
Cheers,
Maxime. -
Thank you so much @m_adam! I will take a deeper look into it and come back if I have any questions, also very good to see that there is a json parser as well already, as I was also planning to send json strings over the web socket
-
Hi @m_adam,
thank you again for the code suggestions, I got it compiling and running now, but now I do have a runtime failure, I am trying to test the websocket connection with postman, and I get a 500 error, the logs of Cinema 4D say this:
<net.maxon.datatype.internedid> net.maxon.http.type : <uint32> 1 <net.maxon.datatype.internedid> net.maxon.http.file : <net.maxon.interface.string-C> index.html <net.maxon.interface.string-C> sec-websocket-extensions : <net.maxon.interface.string-C> permessage-deflate; client_max_window_bits <net.maxon.interface.string-C> sec-websocket-version : <net.maxon.interface.string-C> 13 <net.maxon.interface.string-C> sec-websocket-key : <net.maxon.interface.string-C> j+y86BlBpKXcFzGDp/BOXg== <net.maxon.interface.string-C> host : <net.maxon.interface.string-C> localhost:19923 <net.maxon.datatype.internedid> net.maxon.http.connection : <net.maxon.interface.string-C> Upgrade <net.maxon.interface.string-C> upgrade : <net.maxon.interface.string-C> websocket protocol not found Connection: Stopped with error httpcode [network_websocket_base_impl.cpp(556)] Cause: websocket protocol not found [network_websocket_base_impl.cpp(646)]. httpcode [network_websocket_base_impl.cpp(556)] Cause: websocket protocol not found [network_websocket_base_impl.cpp(646)]
This is my current websocket implementation:
WebSocket::WebSocket(int port) : mPort(port) { } WebSocket::~WebSocket() { maxon::Result<void> res = mWebSocketServer.StopWebServer(); if (res.GetErrorPointer() != nullptr) { ApplicationOutput("Failed to shutdown the websocket!"); } } maxon::Result<void> WebSocket::Start() { iferr_scope; maxon::NetworkIpAddrPort addr(127, 0, 0, 1, mPort); mWebSocketServer = maxon::NetworkWebSocketServerClass().Create() iferr_return; WebSocket* instance = this; mWebSocketServer.ObservableConnected(true).AddObserver( [instance](const maxon::NetworkWebSocketConnectionRef& webSocket, const maxon::DataDictionary& request) -> maxon::Result<void> { return instance->OnConnected(webSocket, request); }) iferr_return; mWebSocketServer.ObservableDisconnected(true).AddObserver( [instance](const maxon::NetworkWebSocketConnectionRef& webSocket) -> void { instance->OnDisconnected(webSocket); }) iferr_return; mWebSocketServer.ObservableHandshake(true).AddObserver( [instance](const maxon::NetworkWebSocketConnectionRef& webSocket, const maxon::DataDictionary& request) -> maxon::Result<String> { return instance->OnHandShake(webSocket, request); }) iferr_return; mWebSocketServer.ObservableMessage(true).AddObserver( [instance](const maxon::NetworkWebSocketConnectionRef& webSocket, maxon::WEBSOCKET_OPCODE opCode, const maxon::BaseArray<Char>& data) -> maxon::Result<void> { return instance->OnMessage(webSocket, opCode, data); }) iferr_return; mWebSocketServer.StartWebServer(addr, false, "<tried different values here but no change>"_s) iferr_return; return maxon::OK; } maxon::Result<void> WebSocket::OnConnected(const maxon::NetworkWebSocketConnectionRef& webSocket, const maxon::DataDictionary& request) { iferr_scope; return maxon::OK; } void WebSocket::OnDisconnected(const maxon::NetworkWebSocketConnectionRef& webSocket) { iferr_scope_handler { return; }; } maxon::Result<String> WebSocket::OnHandShake(const maxon::NetworkWebSocketConnectionRef& webSocket, const maxon::DataDictionary& request) { iferr_scope; return ""_s; } maxon::Result<void> WebSocket::OnMessage(const maxon::NetworkWebSocketConnectionRef& webSocket, maxon::WEBSOCKET_OPCODE opCode, const maxon::BaseArray<Char>& data) { iferr_scope; return maxon::OK; }
Do you know, what could cause this error? Thank you very much for your help already!
-
Hi I do not have access to PostMan so it's hard to reproduce. Would be nice if you can provide a Python/C++/Javascript code that act as a client. So take my answers with a grain of salt.
In my example I used a VsCode plugin (so a NodeJS instance), you can have a look at the client implementation
Cinema-4D-Visual-Studio-Code-Extension .With that's said looking at the error I would say your request header should contain the protocol in the
Sec-WebSocket-Protocol
fields which should match the one you defined inmWebSocketServer.StartWebServer(addr, false, "<tried different values here but no change>"_s) iferr_return;
.
WhileSec-WebSocket-Protocol
is normally not a mandatory field, in our implementation it is mandatory to provide one.Cheers,
Maxime. -
Hi @m_adam, following your typescript example it worked now to connect! Thank you very much for your help
Merry Christmas and a Happy New Year!