dhbaird / easywsclient Goto Github PK
View Code? Open in Web Editor NEWA short and sweet WebSocket client for C++
License: MIT License
A short and sweet WebSocket client for C++
License: MIT License
Hi, I am using this lib to connect to a websocket.
I am getting duplicate messages sometimes, and I don't think the problem is the socket source (who sends the messages). Is there any known bug related to duplicate messages??
I'm on linux and using threads.
EDIT: I was reading the lib code, and I think I found this 'bug'. Actually the handle_message dispatch callback can run after a poll(-1) 'supposedly' blocking (maybe it really is) and return no new message.
The problem is that the dispatch function might not even call the callback function.
I suggest modifying the dispatch function, and make it indicate whether there was a new message read from the socket.
I was assuming that when once poll(-1) returns then a new message was there and it was read with dispatch, but it is not quite the case. The poll function waits on a "select" call and I don't know why it returns even when there isn't exactly a "full message" or something like that, because the dispatch function have conditions to check if we really have a message (or a message that is readable by the current implementation).
Hi David, was just about the start looking at the SSL stuff but we've run into this problem connecting to the Socket.io server... I'm trying to figure out whether it's the client, Socket.io or NodeJS.
I have raised an issue with Socket.io but I'm still not sure it's their side (see link 1). Struggling to debug it.
The problem only affects some clients, so we're trying to figure that too. It connects to the socket.io server ok, handshake done (I will document for you shortly).
However the first disconnect throws this error:
info - socket error Error: read ECONNRESET
at errnoException (net.js:901:11)
at onread (net.js:556:19)
And doesn't clear the session properly (hence I thought it was node or socket.io). When you reconnect the client and disconnect again, you get this:
at Readable.on (_stream_readable.js:679:33)
at Transport.setHandlers (node_modules/socket.io/lib/transport.js:115:15)
at Transport.handleRequest (node_modules/socket.io/lib/transport.js:70:10)
at Transport (node_modules/socket.io/lib/transport.js:31:8)
at new WebSocket (node_modules/socket.io/lib/transports/websocket/hybi-16.js:59:13)
at new WebSocket (node_modules/socket.io/lib/transports/websocket.js:31:17)
at Manager.handleClient (node_modules/socket.io/lib/manager.js:662:19)
at Manager.handleUpgrade (node_modules/socket.io/lib/manager.js:618:8)
NodeJS needs to be restarted to accept a connection again.
Because we have most clients (Linux and Mac) working, I genuinely don't think it's client side. However, you're clearly a socket genius and was hoping you might have some thoughts?
-- EDIT --
We're actually getting this on some Ubuntu builds too.. Still trying to narrow it down..
This is the account of my work.
When i say i need a web socket server in this account i mean i need a web socket server for my work and my boss needs a matured server code. Clean, tested and predictable.
I wanted to contribute in my own time and my own name so i made an account for this and i did.
It is sad you can't see how i improved your code and choose to attack me as a person instead, rejecting perfectly good help.
I will not communicate with you any further because you are too offensive and do not seem to be in touch with reality. Instead i will improve my version more by rewriting all your code and make my own repository. Let the users decide.
ERROR: Could not parse WebSocket url: wss://api.gemini.com/xxx/xxx
This url is from API of a third party, it has been tested scripting languages and web brower(chrome)
It looks like in easywsclient::WebSocket::pointer from_url, it only tried to parse ws:// urls with a xxx:xxx:xxx:xxx format ip? Is it intended?
add onreceivedmessage function to be a handler for messages so that I don't use poll() and dispatch() very many times
Hi!
The connection fails if I connect with "ws://192.168.1.1:3000/websocket". The problem is on easywsclient.cpp at:
if (connect(sockfd, p->ai_addr, p->ai_addrlen) != -1) {
break;
}
I have test it also with this wesocket server: "ws://echo.websocket.org". Same error.
If I test the websocket with a javascript client with the same url all works fine!
Any idea?
Thx
Hi there!
I'm trying to using your library on a personal project, but I can't make it work. I am currently using your classes on a XCode project and always fails here, returning null:
easywsclient.cpp:445
for (i = 0; i < 2 || (i < 255 && line[i-2] != '\r' && line[i-1] != '\n'); ++i) {
if (recv(sockfd, line+i, 1, 0) == 0) {
return NULL;
}
}
Any idea?
I'm not able to get work my program with TinyThread. In the main() I start the connection and a thread makes the loop with poll and dispatch. Any idea?
Regards
when receiving dataframes with size (ws.N0 == 127), then parsing is somehow not working -- I tried looking into the header size, since it seems it should be 10 and not 8 according to specs, however this is not enough, somehow parsing fails -- has anybody tested this lib with large dataframes?
For some reason, I'm getting a disconnection every 85 seconds while using connection->poll
Have tried experimenting with different sleeps but the socket religiously disconnects itself after 85 counts (seconds).
I've created an exception to catch this and restart the server but it's not a long term solution.
Could you shed any light on why this might be happening?
Rather than have easywsclient depend directly on a specific socket implementation, instead inject the socket implementation. Reasons why this is needed:
Here's some pseudocode along these lines...
class Socket // just a normal TCP socket, not a websocket. This is what gets injected.
{
// return codes follow the BSD/POSIX convention:
enum { EWOULDBLOCK = ..., ... };
virtual void close() = 0;
virtual ssize_t recv(void *buf, size_t len) = 0;
virtual ssize_t send(const void *buf, size_t len) = 0;
virtual void select(int millisecondsTimeout) = 0;
};
class RealWebSocket : public easywsclient::WebSocket
{
// Now inject a Socket object instead of a socket file descriptor:
RealWebSocket(auto_ptr<Socket> socket, ...);
};
// This is the factory: the function responsible for creating and injecting all dependencies:
WebSocket::pointer WebSocket::from_url(const std::string& url, const std::string& origin)
{
if ("wss:" == url.substr(0, 4)) {
auto_ptr<OpenSslSocket> socket(new OpenSslSocket(...));
return new RealWebSocket(socket, ...);
}
else { // assume ws:
#ifdef _WIN32
auto_ptr<WinsockSocket> socket(new WinsockSocket(...));
#else
auto_ptr<PosixSocket> socket(new PosixSocket(...));
#endif
return new RealWebSocket(socket, ...);
}
}
New to dependency injection? Miško Hevery explains it best: The Clean Code Talks - Don't Look For Things!. It is also described on the Wikipedia SOLID page.
I get this error on connecting to firefox websocket remote debugger i launch firefox with :
firefox -start-debugger-server ws:6047
the I tried to connect to ws://localhost:6047 using wscat from npm and it connected successfully but when trying to connect using easywsclient with this code :
auto ws = WebSocket::from_url_no_mask("ws://localhost:6047");
I got this in the terminal :
easywsclient: connecting: host=localhost port=6047 path=/
getaddrinfo: E
ERROR: Got bad status connecting to ws://localhost:6047:
Hello and thank you for sharing easywsclient,
right now easywsclient does not allow us to wait for arriving data via a nonblocking recv or select/poll. I guess some projects might be ok with that, but actually this is kinda illogical. The point of Websockets is to avoid active polling and hence, it would be nice if easywsclient provides a mechanism (e.g. a blocking function) to wait for data.
Best regards,
Donald Pillou
In all of your snprintf statements after the Host: line you pass host and port, but the format string takes 0 parameters.
does this library support windows 7
Hi there,
I've started using this code and it's pretty awesome how easy it was to integrate in our current C++ project. There is one that is missing though is that I would like to have callbacks being executed when some operation happens; here are 2 examples:
This would help us propagate that info upstream to other piece of the code, for example so that we can display a dialog such as "reconnecting", or "disconnected" when we are ... disconnected.
This can probably be implemented without breaking backward compatibility, by registering additional callbacks, or maybe only one that will work with enums or such.
Also the errors that are printed on the terminal through fprintf cannot be used by other code. I wonder if there should be some kind of callback there. Some function could return error messages as std::string by reference too, which is a pattern I've often used to propagate errors. Although this doesn't work very well when the error is too "far" from the call site.
I might try to prototype something and send a Pull request, but I wanted to get the maintainer opinion first before I go in a wrong direction, because I would love this work to be merged in if it is generic enough.
Thanks !
Tried to use this code in visual c++, it doesn't work because many #include statements are not designed for windows or visual c++ ? Getting two screen of errors.
Tried to compile it with g++.
At first, got an error about IPPROTO_TCP not being defined, so I had to include "netinet/in.h" into your code.
Now I'm getting this:
easywsclient/easywsclient.cpp: In member function `virtual void easywsclient::_RealWebSocket::send(std::string)':
easywsclient/easywsclient.cpp:245: warning: right shift count >= width of type
easywsclient/easywsclient.cpp:246: warning: right shift count >= width of type
easywsclient/easywsclient.cpp:247: warning: right shift count >= width of type
easywsclient/easywsclient.cpp:248: warning: right shift count >= width of type
/tmp/cc9deA2J.o: error: undefined reference to '_connect'
The last error comes from this line of code
easywsclient/easywsclient.cpp:41
if (connect(sockfd, p->ai_addr, p->ai_addrlen) != -1) {
If I comment it out it compiles but apparently doesn't connect to a socket...
Do you know how to make it all work?
Hi,
is there way to handle onClose, onError ?
i would like to reconnect in case of onClose, onError.
thanks
Hi,
I was exercising easywsclient using the test code and one of my use cases requires a short-lived websocket where the client will issue a close. When I tested it, I noticed that my server never received the close message prior to the client exiting. In looking at the source, it appears that the buffer with the close message is constructed but never sent. I am not a C++ dev, but it looked to me like I needed to add the send, etc. (int ret = ::send(sockfd, (char*)&txbuf[0], txbuf.size(), 0);) I did so in my test program and it worked - the server now sees the close message come through. Could you please incorporate this/a fix in for this issue when you have a chance as I am not sure that my fix implementation is complete?
Regards,
Chris Whelan
void close() {
if(readyState == CLOSING || readyState == CLOSED) { return; }
readyState = CLOSING;
uint8_t closeFrame[6] = {0x88, 0x80, 0x00, 0x00, 0x00, 0x00}; // last 4 bytes are a masking key
std::vector<uint8_t> header(closeFrame, closeFrame+6);
txbuf.insert(txbuf.end(), header.begin(), header.end());
int ret = ::send(sockfd, (char*)&txbuf[0], txbuf.size(), 0);
if (ret <= 0) {
closesocket(sockfd);
readyState = CLOSED;
fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr);
}
}
Dear mister Baird,
I would like to help by cleaning up the code and make a neat object from the easy web socket.
It would be a big change but IMHO a big improvement.
Using it, debugging it and updating it and reading the code would be a lot easier.
Using it would be a bit different though.
Are you open for such a commit? It probably would be done in 3 days.
Regards
Hi all,
I just want to know, can we implement OAuth in websockets ?. If so how can we do this to make web sockets more secure. If anybody has idea on how to add this support, please share with me.
Thanks and Regards,
Vinod Kumar
Hi!
awesome Client! I was able to implement it.
Does anyone have an example how to receive messages? I am having trouble using the dispatch().
Thanks!
Cover TinyThread++, Boost.Thread, (Intel TBB? std::thread?).
becuase i see
#include <WinSock2.h>
i have compiled example-client.cpp as per readme.
i am not doing anything else on received messages, just doing cout on received messages.
and the server usage is 100%
any idea why is that ?
p.s. this is by far easiest websocket /clientlibrary i have seen.
thanks
Hello. I tried using the example-client, but I ran into an issue when asserting ws. I took the client and replaced the url with:
ws = WebSocket::from_url("wss://ws-feed.gdax.com");
I get the following output:
ERROR: Could not parse WebSocket url: wss://ws-feed.gdax.com
example-client: example-client.cpp:37: int main(): Assertion `ws' failed.
Aborted
Hi!
How can I set the "ws" variable globally in my code?
#include "easywsclient.hpp"
#include "easywsclient.cpp" // <-- include only if you don't want compile separately
#include <assert.h>
#include <stdio.h>
#include <string>
using easywsclient::WebSocket;
static WebSocket::pointer ws = NULL;
void handle_message(const std::string & message)
{
printf(">>> %s\n", message.c_str());
if (message == "world") { ws->close(); }
}
int main()
{
ws = WebSocket::from_url("ws://localhost:8126/foo");
assert(ws);
ws->send("goodbye");
ws->send("hello");
while (ws->getReadyState() != WebSocket::CLOSED) {
ws->poll();
ws->dispatch(handle_message);
}
return 0;
}
This don't work! The console returns me:
error: reference to 'ws' is ambiguous
Thx :)
Hi,
I am having trouble implementing the WebSocket Client in TwinCat C++. I don't exactly understand how I should proceed, can anyone help me?
Hey there.
I am urgently trying to get a Websocket client working in PHP...and it is starting to pain my head - last solution, native module. So I have seen how people wrote "bindings to native libraries" and even examples on how to turn a project's functions into PHP functions via a module. Ok, sounds cool. So I think maybe this little piece of code is my solution.
The thing is, I am not a PHP-extension coder. Would you help me on getting this up? Its not like I can not code, but i am ultranew to c++ xD
Regards, Ingwie
Dear Author,
Great work on this- I was able to compile this module on a toolchain
Continuing along, I am not able to connect to WSS sever. How can I make this to code work for SSL support. What extra library is required for this.
I installed openssl-util, libopenssl as well but not able to succeed
Any advice on this greatly appreciated
Sincere Regards,
BREL
Hi dhbaird,
It seems we cannot receive binary data with your library...
Is it me who didn't manage to work with correctly or it is true ?
(i sent you an email about that btw)
"ERROR: Got unexpected WebSocket message." when trying to receive binary data
from line 315 of easywsclient.cpp
Do you think you can help me in any way ?
thx
When trying to send this string:
{"action":"update_state","payload":"AAAAAMzMzMxNGmLY1H8+QP5ii+m4oFXASc3y6bEFaEAaCsUgRmrCv2vYrK5+3Xq/AN/IeeM0Mj8AQNtpk4OgvgCAUXNX3Vc/n5bAlyZReD+wcAa4kTIWP8G7Y97RYFlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8///UWd5eD+a168NGC5XvgAAACCVQmw/AAAAAMzMzMw=","type":"sim_packet"}
I get error Expression: vector iterator not dereferencable
. I happens in easywsclient.cpp in sendData function at line 413:
if (useMask) { for (size_t i = 0; i != message_size; ++i) { *(txbuf.end() - message_size + i) ^= masking_key[i&0x3]; } }
Then call stack points to vector operator*().
Any ideas why this happens? Other messages are sent without issues.
Easywsclient works in Linux at the moment. Let's get it working in Windows and Winsock, too.
I'm trying to connect to chrome remote debugger , this works on windows 7 and 8 but on windows 10 I get this :
ERROR: url size limit exceeded : ws://localhost:4789/devtools/page/(b11a4f7-e269-41c1-8cd5-9b077e0df2ff"
I want to send an array like this to chrome after I connected to it using websockets and remote debugging
{
id: 2,
method: 'Page.navigate',
params: {url: 'http://twitter.com/#!/search/chrome?q=chrome&' + rand(100).to_s}
}
if I call "from_url" whith a non exist ip adress, it will block in easyclient.cc:102. more than two minutes!
Hi,
It seems that when I close a socket I get an PSH ACK (acknowledgement) packet (after the fact) that isn't handled and then the router issues a RST packet.
I'm a bit concerned this will come to be a bigger issue in prolonged usage. I tried to reproduce with python & jquery and it does not seem to be a server issue but directly related to when I use easywsclient.
I think ws->close() does not handle the ACK packet and closed prematurely. Any thoughts on how to mitigate this?
When interface is localhost, "ws://localhost/foo" for instance
function connect returns SOCKET_ERROR on first try. On second try everything is fine.
"if (connect(sockfd, p->ai_addr, p->ai_addrlen) != SOCKET_ERROR)..."
And this happens only with localhost interface
The result is a timeout delay, because of the first error.
tested on windows and mac os
Would be awesome if this handled JSON out of the box!
I managed to have it almost working on MVS2013 Express
At compile time I got errors for each sscanf then I had to define
After that the following errors persisted:
Error 1 error C2664: 'int recv(SOCKET,char *,int,int)' : cannot convert argument 2 from 'int8_t *' to 'char *' easywsclient.cpp 183
Error 2 error C2664: 'int send(SOCKET,const char *,int,int)' : cannot convert argument 2 from 'int8_t *' to 'const char *' easywsclient.cpp 206
Then I added a cast (char )(int8_t *) in place of just (int8_t), resulting in :
::send(sockfd, (int8_t*)&txbuf[0], txbuf.size(), 0);
The two hacks above ensure compiling of the easywsclient.cpp and .hpp files.
I managed to compile example-client.cpp and have it working, sending and receiving handshake.
When I send the first packet after handshaking I receive from the server the error
"protocol error" because the frame is unmasked, then the connection is closed.
I suppose that this has something to do with the cast mentioned above, whiche doesn't send the correct data to the server.
If I disable the check for unmasked frame in the server, the connection stays alive and example-client can receive and display messages sent by the server.
I managed to compile example-client-cpp11.cpp but as I execute it , it prints
easywsclient: connecting:host=10.0.2.2 port=8080 parth=/demo
getaddrinfo: Either the application has not called WSAStartup or WSAStartup failed.
(Note that I'm a bit noobish to c++, especially when it comes to using other people's libraries.)
Using example windows code (with the included cpp), the program broke on run time.
Here's the full error:
||=== Build: Debug in ClientTest (compiler: GNU GCC Compiler) ===|
easywsclient.cpp|12|warning: ignoring #pragma comment [-Wunknown-pragmas]|
main.cpp|4|warning: ignoring #pragma comment [-Wunknown-pragmas]|
easywsclient.cpp|33|error: conflicting declaration 'typedef char int8_t'|
stdint.h|27|error: 'int8_t' has a previous declaration as 'typedef signed char int8_t'|
easywsclient.cpp|35|error: conflicting declaration 'typedef long int int32_t'|
stdint.h|31|error: 'int32_t' has a previous declaration as 'typedef int int32_t'|
easywsclient.cpp|36|error: conflicting declaration 'typedef long unsigned int uint32_t'|
stdint.h|32|error: 'uint32_t' has a previous declaration as 'typedef unsigned int uint32_t'|
easywsclient.cpp||In function 'socket_t {anonymous}::hostname_connect(const string&, int)':|
easywsclient.cpp|92|error: '_snprintf_s' was not declared in this scope|
easywsclient.cpp|93|error: 'getaddrinfo' was not declared in this scope|
easywsclient.cpp|108|error: 'freeaddrinfo' was not declared in this scope|
easywsclient.cpp||In function 'easywsclient::WebSocket* {anonymous}::from_url(const string&, bool, const string&)':|
easywsclient.cpp|468|error: '_snprintf_s' was not declared in this scope|
||=== Build failed: 10 error(s), 2 warning(s) (0 minute(s), 1 second(s)) ===|
It's probably me doing something stupid, but I was just wondering what went wrong here. (Again, using the example code and including the cpp file.)
Windows 10, using Code Blocks.
From the "example-client.cpp" i see that in order to handle received data from server i need busy wait:
while (ws->getReadyState() != WebSocket::CLOSED) {
ws->poll();
ws->dispatch(handle_message);
}
Is there some interrupt/callback mechanism to handle rx instead?
Thanks.
The frame that is sent by the WebSocket->close() function contains a total of 4 bytes. 0x88 0x00 0x00 0x00 .
0x88 is the correct heading byte for a close connection message.
0x00 denotes a non-masked message of application-data size 0
therefore, the two following bytes shouldn't be there because the data size is defined as 0.
They are interpreted (by wireshark) as a continuation unmasked frame of size 0 (which is against the RFC6455 bit "The application MUST NOT send any more data frames after sending a Close frame.").
Such specification states also that there may be some application data in a closing frame, but it should be formatted as described:
" The Close frame MAY contain a body (the "Application data" portion of
the frame) that indicates a reason for closing, such as an endpoint
shutting down, an endpoint having received a frame too large, or an
endpoint having received a frame that does not conform to the format
expected by the endpoint. If there is a body, the first two bytes of
the body MUST be a 2-byte unsigned integer (in network byte order)
representing a status code with value /code/ defined in Section 7.4.
Following the 2-byte integer, the body MAY contain UTF-8-encoded data
with value /reason/, the interpretation of which is not defined by
this specification. This data is not necessarily human readable but
may be useful for debugging or passing information relevant to the
script that opened the connection. As the data is not guaranteed to
be human readable, clients MUST NOT show it to end users."
(RFC 6455)
Furthermore, even though it may sound strange:
" Close frames sent from client to server must be masked" (RFC 6455)
Implementing all of these specifications is easy and would give us a more standard-compliant platform.
Would it be difficult to create a easywsserver?
What would have to be changed to this code to do this?
I needed these features: an infinite timeout and a faster socket death detection.
If you are interested, here are the patches:
--- /Users/wpelissero/tmp/easywsclient/easywsclient.cpp 2015-08-20 12:49:06.000000000 +0200
+++ easywsclient.cpp 2015-08-28 17:58:17.000000000 +0200
@@ -188,15 +188,25 @@
}
return;
}
if (timeout > 0) {
if (timeout != 0) {
fd_set rfds;
fd_set wfds;
fd_set efds;
timeval tv = { timeout/1000, (timeout%1000) \* 1000 };
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
FD_SET(sockfd, &rfds);
if (txbuf.size()) { FD_SET(sockfd, &wfds); }
select(sockfd + 1, &rfds, &wfds, NULL, &tv);
FD_SET(sockfd, &efds);
select(sockfd + 1, &rfds, &wfds, &efds, timeout > 0 ? &tv : 0);
if (FD_ISSET(sockfd, &efds))
{
closesocket(sockfd);
readyState = CLOSED;
fputs("Connection error!\n", stderr);
return;
}
}
while (true) {
// FD_ISSET(0, &rfds) will be true
encoding Chinese
ws->send("哈哈");
node.js console:
����
thinks.
This is a question and I hope this is the right place for this. I had a need for a C++ non static member function as callback. Being relatively new to C++ Google lead me to using a "trampoline" approach to get around this, i.e. declare a global callback function and then pass through a pointer to the instance of the class using void*. Then static_cast this pointer back to the correct class type. Much like this.
This however required me to add this second parameter to the dispatch signatures in the easywsclient.hpp class. Is there another way to do this without changing the code?
I'm using C++98 with GNU compiler.
Hi -
Thanks for the work on this library. It's certainly the easiest-to-consume C++ WS library I've come across.
One thing that I don't see is a way to determine whether an incoming message is binary or text. The _dispatchBinary
method gets the opcode, but then just eats it without telling the callable
. It seems like it would make sense to pass the type on as well.
Or maybe I'm missing something in the way things are supposed to work.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.