Introduction
If your network Symbian program just blocks forever mysteriously, or you just want a simple piece of code to demonstrate how to use socket in Symbian. This story is for you.
The expedition
Very often, we want to read a given number of bytes from a network when, for example, the message body length is known after reading the header. Symbian RSocket
provides two groups of reading methods, RecvOneOrMore()
with a scheme for reading some and returning as soon as possible and Recv()
/Read()
with a scheme for blocking until the maximum length of the buffer descriptor is fully filled. Both the schemes can fulfill our requirement. With RecvOneOrMore
, we can make a loop to keep reading and counting the returned bytes until the sum is not less than the desired message length. Obviously, this method is not efficient enough. We may prefer to allocate a descriptor with a given maximum length and use Recv()
/Read()
to get the work done in one call. The example is as follows:
RSocketServ socketServ;
RSocket socket;
RSocket listener;
User::LeaveIfError(socketServ.Connect());
CleanupClosePushL(socketServ);
User::LeaveIfError(listener.Open(socketServ,
KAfInet,KSockStream, KProtocolInetTcp));
User::LeaveIfError(listener.SetLocalPort(80));
User::LeaveIfError(listener.Listen(1));
TRequestStatus status;
TBuf8<256> buffer;
socket.Open(socketServ);
listener.Accept(socket, status);
User::WaitForRequest(status);
......
TBuf8<256>data;
socket.Read(data,status);
User::WaitForRequest(status);
......
The listener
object listens at port 80, once an incoming connection is accepted; another RSocket
object socket
reads the data from the network as shown in the last three lines. Here, 256 bytes are read before the code can continue.
This is not the end of the story because while we are coding, we usually have no idea if it must be 256. The number of bytes to be read can be known only at run time. But the size of the stack based descriptor TBuf
must be set before compilation. Unfortunately, I couldn't find any piece of example code that solves this problem in SDK document or Google. I tried to use the heap based alternative, HBufC
, whose size can be set at run time. HBufC
is not modifiable. But RSocket::Read
method requires a modifiable descriptor. This can be overcome by using the method HBufC::Des()
. This gives a good introduction to the Symbian descriptor system. The code is modified as follows:
TPtr8 gDataPtr(NULL,0);
UInt32 msglen;
HBufC8* buffer = HBufC8::NewL(msglen);
gDataPtr.Set(buffer->Des());
socket.Read(gDataPtr,status);
User::WaitForRequest(status);
It works as expected. But is it correct? I thought so before it took me a whole day to find out why sometimes the program just blocks forever. For example, when the value of msglen
is 137 and when you check buffer->Des().MaxLength()
of the newly allocated buffer
, you get a value of 140! The RSocket::Read
method checks only the MaxLength and tries to fill it. It keeps waiting for the 3 bytes that never exist even when the desired 137 bytes have already been received.
Why the MaxLength is 140 and not 137? Because “the maximum length of the heap cell in which the HBufC
was allocated is used to set the maximum length of the returned TPtr
", as Tip9 of descriptor-tips says. The size of the heap cell only guarantees that it can contain the size given by the user, but not equal to it! 137 is not word-aligned, so the system allocates 140 instead.
I can’t imagine that the Symbian descriptor system doesn’t provide any solution for such a simple problem to the RSocket::Read
’s requirement. However, I am too exhausted to dig for it. I finally chose a non-Symbian favour by maintaining a C-style array.
TUint8 *buf=new TUint8[msglen];
gDataPtr.Set(buf,0,msglen);
blank.Read(gDataPtr,status);
User::WaitForRequest(status);
Of course, now I have to take care of releasing the array space myself.
I'll appreciate if any Symbian expert can give us an official solution. My expedition has to stop here before I can’t help deleting my Symbian SDK and install double copies of Java and .NET.