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:
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.