Low level async send with callback.

222 views
Skip to first unread message

Jonas Bjering

unread,
Aug 21, 2014, 9:16:08 AM8/21/14
to webso...@googlegroups.com
Hi,
Posted this question on the irc channel, but as noone seemed to be active I do as suggested in the topic there and post here instead.

We want to use wewbsocketpp in an existing application. 

Unfortunaly in our architechtrue we have split responsibility between a fairly high level connection abstraction (that among other things handles a send queue) and a low level socket abstraction, which essentially only contains an async_write method. Now the easiest way for us to incorporate websocketpp would be to make a new implementation of our lowlevel "socket" abstraction.

However the stumbling block here is that websocketpp connection objects wants to do the send message queuing themselves, and thus the send method do not allow us to pass a callback method (which would allow us to implement our asynch_write method with it).

Are we missing something? Are we trying to do something very wrong? Or is this just a use-case that just happens to not be supported. I guess looking at the source that adding a callback whenever the websocketpp send queue becomes empty looks fairly straightforward. Do you see any reason not to do this?

Peter Thorson

unread,
Aug 21, 2014, 10:41:19 AM8/21/14
to Jonas Bjering, webso...@googlegroups.com
If you have your own socket implementation and just need to read/write a websocket byte stream take a look at the iostream transport or writing your own transport policy. The iostream transport policy interface has a method that lets you feed in new bytes that you read off your socket and a method where you register an ostream that gets written to when there is new data to write back to the socket. I’ve had a few requests for a straight up callback with a buffer of bytes to write as an option in addition to the registered ostream, let me know if that would interest you.

If you want something lower level and more custom than iostream transport you can write your own transport policy. This is less well documented on the master/release branch, but the develop branch includes a stub transport policy that demonstrates the interface that would need to be implemented (https://github.com/zaphoyd/websocketpp/tree/develop/websocketpp/transport/stub). The meat of it is an async_read_at_least method for reading at least n bytes from a socket and async_write for writing a specific buffer to the socket.

With respect to queuing, WebSocket++ will queue messages sent using endpoint::send(…) or connection::send(…) only if the underlying transport presents an asynchronous interface to WebSocket++. With the bundled asio transport, which does present an asynchronous interface, endpoint::send will queue its message, signal the message writing strand to start, then return. If send is subsequently called additional messages will be queued. These messages will be batched and when the writing strand actually ends up running all queued messages will be written rather than one at a time. This drastically improves performance when sending lots of small messages on the same connection.

When using the iostream transport, or a custom transport that does not present an asynchronous api, each send call blocks until the transport policy has dealt with the message. In this mode the WebSocket++ send queue is always empty and it is the responsibility of the code either upstream or downstream from WebSocket++ to perform any queuing desired.

--
You received this message because you are subscribed to the Google Groups "WebSocket++" group.
To unsubscribe from this group and stop receiving emails from it, send an email to websocketpp...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jonas Bjering

unread,
Aug 21, 2014, 11:15:49 AM8/21/14
to webso...@googlegroups.com, depicti...@gmail.com
Hi,
Thank you for your response. I will take a look at the transport policies as you mention. I just feel perhaps I did not get across our situation very well (mostly due to us borrowing the name socket to mean something slightly different than socket... thus my quote marks in the first post..)

To clarify I would like to add more specific details.

We are porting a C++ server that used to support Flash-clients to instead support HTML-clients using websocket. The server is fully async and is built using boost::asio, we are very happy to have found websocketpp.

We will only be interested in the async + tls option.

In our existing codebase (the one that talks to Flash-clients) we have separated the network code into two kind of objects, the IConnection that handles queing for sends and have a circular read buffer (we have a header with size, and then a binary body containing a zlib-compressed google protobuf message). It also handles some flash-idiosyncracies (with a <policy-file-request> sometime arriving before the TLS handshake..).

The interface to this object looks like this:


class IConnection
{
public:
  virtual ~IConnection() {}

  virtual bool isNullConnection() const = 0;

  virtual void startRead(boost::weak_ptr<IConnectionHandler> handler) = 0;
  virtual void send(const boost::shared_ptr<Message>& msg) = 0;
  virtual void disconnect() = 0; // This operation is always asynchronous, ie the handler onDisconnect is posted on the io

  virtual bool isConnected() const = 0;
  virtual std::string remoteIp() const = 0;
};

with startRead(...) and send(...) being the most important methods. This interface may look very similiar to your websocketpp connection but since we put a few higher level things in there to (our own binary framing + zlib + protobuf), the minimal change to our (by now stable and proven) codebase would be to only change the lower level constuct. The "socket". This is really a very thing wrapper around boost::asios async methods and we put it there to have a runtime polymorphism vs plain sockets and SSL sockets (and also to enable unittesting). 

The socket have this interface:

class ISocket
{
public:
  virtual ~ISocket() {}

  virtual std::string remoteIp() const = 0;

  virtual void asyncWrite(
    const std::vector<unsigned char>& buf, 
    int noOfbytesToSend,
    boost::function<void (const boost::system::error_code& error, size_t bytesTransferred)> callback) = 0;

  virtual void asyncReadSome(
    MutableBufferSequenceAdapter& buf,
    boost::function<void (const boost::system::error_code& error, size_t bytesTransferred)> callback) = 0;
  
  virtual void close() = 0;

  virtual boost::asio::io_service& getIoService() = 0;
};

with asyncWrite and asyncReadSome beeing the important ones. MutableBufferSequenceAdapter is a helper that maps boost::asio buffers to google protobufs ZeroCopy buffer.

Now I rephrase my question to: Do you think it is a good idea to make a new implementation of our ISocket that wraps websocketpp connection or do we need to attack this on the level of our IConnection. And if we implement the ISocket, what construct should we use (connection seems to high level as it adds queuing and do not provide a callback from its send method). 

You probably answered this in your first response and I just have to read up on it and think :) 
I just wanted to post this more detailed explanation of our problem in case it helps.

Regards,
Jonas
Reply all
Reply to author
Forward
0 new messages