Symbian: RSocket::Recv and HBufC8

A common way to do stream-based network communication in Symbian OS is with the RSocket class. The RSocket::Recv(TDes8 &aDesc, …) method “only completes when the full amount of requested data has been received (or the connection breaks). This means when the descriptor has been filled to its maximum length (not its current length).”

This is handy if an application receives messages of unknown length, but those messages include a header that includes the length. E.g., always receive 4 bytes of header which encode the amount of payload data. What seems like a sensible thing to do is dynamically allocate a buffer descriptor to hold exactly the expected payload size. This seems nice because then the OS worries about reassembling fragmented or long messages.

Unfortunately, note that the heap descriptor of choice, HBufC8, when allocated with HBufC8::NewL(TInt aMaxLength), may not be the expected size: “Note that the resulting heap cell size and, therefore, the resulting maximum length of the descriptor may be larger than requested.” This is a problem, because it causes RSocket::Recv() to hang waiting for more data than the remote end has sent.

I’m aware of three work-arounds, and zero elegant solutions:

  • RSocket::RecvOneOrMore and manage buffers ourselves (more work: yuck).
  • Allocate a C-style array and don’t use Symbian descriptors. This page talks about that solution.
  • Cast the const pointer available for an HBufC8 and create a Tptr8 manually, assigning its max size to be precisely the message size. This page talks about that solution.

Currently I’m using the cast-away-the-const solution. Note that the HBufC8’s length will not be modified, so it must be set manually:

HBufC8 *iBuffer;
TPtr8 iBufferPtr;
...
iBufferPtr.SetLength(payloadSize); // without this iBuffer's length remains 0
iBufferPtr.Set((TUint8*)iBuffer->Des().Ptr(), 0, payloadSize);

My understanding is that SetLength() sets the current length of iBuffer. Set() sets the current length and maxlength of the TPtr8 object. Thus, RSocket sees the current length of 0 and max length of payloadSize, and behaves as expected. Code processing the iBuffer in some class’s RunL() will see the length that was set with SetLength(), but since RSocket is no longer operating with a mistaken notion of the desired amount of data to read, RunL() will only be called when the right amount of data has arrived.

It’s too late and I’m too tired to make this more concise. Forgive me.

UPDATE: It seems that an RBuf8 will solve this problem elegantly, but I haven’t had time to try it yet.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s