Click here to Skip to main content
Click here to Skip to main content

Reading given number of bytes with RSocket::Read()

, 14 Jul 2005
Rate this:
Please Sign up or sign in to vote.
Exploring how to read given number of bytes with RSocket::Read() on Symbian.

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;
    //... assign value to 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

luweiewul

Germany Germany
No Biography provided

Comments and Discussions

 
Generalmaybe you are right Pinmemberwaynechen19-Sep-05 22:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140902.1 | Last Updated 14 Jul 2005
Article Copyright 2005 by luweiewul
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid