使用 RSocket::Read() 读取给定字节数
探索如何在 Symbian 上使用 RSocket::Read() 读取指定数量的字节。
引言
如果你的网络 Symbian 程序总是神秘地无限期阻塞,或者你只是想要一段简单的代码来演示如何在 Symbian 中使用套接字,那么这篇文章就是为你准备的。
探索之旅
很多时候,我们需要从网络读取指定数量的字节,例如,在读取消息头后知道消息体长度。Symbian RSocket
提供了两组读取方法,RecvOneOrMore()
方案是读取一些数据并尽快返回,而 Recv()
/Read()
方案是阻塞直到缓冲区描述符的最大长度被完全填满。这两种方案都可以满足我们的需求。使用 RecvOneOrMore
,我们可以创建一个循环来不断读取并计数返回的字节,直到总和不小于所需的消息长度。显然,这种方法效率不够高。我们可能更喜欢分配一个具有给定最大长度的描述符,并使用 Recv()
/Read()
在一次调用中完成工作。示例如下
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); ......
listener
对象监听端口 80,一旦接受传入连接,另一个 RSocket
对象 socket
从网络读取数据,如最后三行所示。在这里,读取 256 个字节后,代码才能继续执行。
故事还没结束,因为在编写代码时,我们通常不知道它必须是 256。要读取的字节数只能在运行时知道。但是基于栈的描述符 TBuf
的大小必须在编译之前设置。不幸的是,我在 SDK 文档或 Google 中找不到任何解决此问题的示例代码。我尝试使用基于堆的替代方案 HBufC
,其大小可以在运行时设置。HBufC
是不可修改的。但是 RSocket::Read
方法需要一个可修改的描述符。这可以通过使用方法 HBufC::Des()
来克服。这个 对 Symbian 描述符系统进行了很好的介绍。代码修改如下
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);
它按预期工作。但它正确吗?我以前是这么想的,直到我花了一整天的时间才弄清楚为什么有时程序会无限期阻塞。例如,当 msglen
的值为 137 时,当你检查新分配的 buffer
的 buffer->Des().MaxLength()
时,你会得到 140 的值!RSocket::Read
方法只检查 MaxLength 并尝试填充它。它一直在等待永远不会存在的 3 个字节,即使已经接收到所需的 137 个字节。
为什么 MaxLength 是 140 而不是 137?因为“用于分配 HBufC
的堆单元的最大长度用于设置返回的 TPtr
的最大长度”,如 descriptor-tips 的 Tip9 所述。堆单元的大小只保证它可以包含用户给定的大小,但不等于它!137 没有字对齐,因此系统分配了 140。
我无法想象 Symbian 描述符系统没有为 RSocket::Read
的要求提供任何解决这样一个简单问题的方法。但是,我太疲惫了,无法深入挖掘它。我最终选择了一种非 Symbian 的方式,即维护一个 C 风格的数组。
TUint8 *buf=new TUint8[msglen]; gDataPtr.Set(buf,0,msglen); blank.Read(gDataPtr,status); User::WaitForRequest(status);
当然,现在我必须自己负责释放数组空间。
如果任何 Symbian 专家能给我们一个官方解决方案,我将不胜感激。我的探索必须在这里停止,否则我忍不住要删除我的 Symbian SDK 并安装双份的 Java 和 .NET 了。