CocoaAsyncSocket 并从套接字读取数据
在基于 TCP 套接字的服务器上,我通过流发送数据包,其中数据包包含指定数据包中字节数的标头,后跟该字节数。对于熟悉 Erlang 的人来说,我只是设置 {packet, 4} 选项。在 iOS 端,我的代码看起来像这样,假设我想计算出此消息的流的大小:
[asyncSocket readDataToLength:4 withTimeout:-1 tag:HEADER_TAG];
工作正常,并且调用以下委托方法回调:
onSocket:didReadData:withTag:
我认为下一个逻辑步骤是计算出 在服务器端对 12 字节的字符串进行
UInt32 readLength;
[data getBytes:&readLength length:4];
readLength = ntohl(readLength);
硬编码后,readLength 确实也在客户端读取了 12,所以到目前为止一切都很好。我继续执行以下操作:
[sock readDataToLength:readLength withTimeout:1 tag:MESSAGE_TAG];
此时,不再调用回调 onSocket:didReadData:withTag: 。相反,读取超时,可能是因为我没有正确处理读取,因此调用了此委托方法:
- (NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length
因此,服务器总共发送 16 个字节、一个 4 字节标头和一个 12 字节二进制流。
我确信错误出在我如何使用 CocoaAsyncSocket 上。在确定流的大小后,读取流的其余部分的正确方法是什么?
** 更新 **
我更改了我的客户端,它现在似乎可以正常工作。问题是,我不明白新解决方案的 readDataToLength 的意义。以下是我将最初的阅读更改为:
[socket readDataWithTimeout:-1 tag:HEADER_TAG];
现在在我的回调中,我只需执行以下操作:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
if (tag == HEADER_TAG) {
UInt32 readLength;
[data getBytes:&readLength length:4];
readLength = ntohl(readLength);
int offset = 4;
NSRange range = NSMakeRange(offset, readLength);
char buffer[readLength];
[data getBytes:&buffer range:range];
NSLog(@"buffer %s", buffer);
//[sock readDataToLength:readLength withTimeout:1 tag:MESSAGE_TAG];
} else if (tag == MESSAGE_TAG) {
//[sock readDataToLength:4 withTimeout:1 tag:HEADER_TAG];
}
}
因此,所有内容都作为一个原子有效负载进入。也许这是因为 Erlang {packet, 4} 的工作方式。我希望是这样。否则,readDataToLength 的意义何在?客户端无法提前知道消息的长度,那么使用该方法的良好用例是什么?
On my TCP-socket based server, I send a packets over the stream where packets consist of a header specifying the number of bytes in the packet, followed by that number of bytes. For those familiar with Erlang, I'm simply setting the {packet, 4} option. On the iOS side, I have code that looks like this, assuming I want to figure out the size of the stream for this message:
[asyncSocket readDataToLength:4 withTimeout:-1 tag:HEADER_TAG];
That works fine and the following delegate method callback is invoked:
onSocket:didReadData:withTag:
I figure the next logical step is to figure out the size of the stream, and I do that with:
UInt32 readLength;
[data getBytes:&readLength length:4];
readLength = ntohl(readLength);
After hard coding a string of 12 bytes on the server-side, readLength does indeed read 12 on the client also, so all is good so far. I proceed with the following:
[sock readDataToLength:readLength withTimeout:1 tag:MESSAGE_TAG];
At this point though the callback onSocket:didReadData:withTag:
is no longer invoked. Instead timeouts on the read are occurring, probably because I didn't handle the read properly, this delegate method gets invoked:
- (NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length
so in total, the server is sending 16 bytes, a 4 byte header and a 12 byte binary stream.
I'm confident that the error is on how I'm using CocoaAsyncSocket. What's the right way to go about reading the rest of the stream after I figure out its size?
** UPDATE **
I changed my client and it seems to be working now. The problem is, I don't understand the point of readDataToLength with the new solution. Here's what I changed my initial read to:
[socket readDataWithTimeout:-1 tag:HEADER_TAG];
Now in my callback, I just do the following:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
if (tag == HEADER_TAG) {
UInt32 readLength;
[data getBytes:&readLength length:4];
readLength = ntohl(readLength);
int offset = 4;
NSRange range = NSMakeRange(offset, readLength);
char buffer[readLength];
[data getBytes:&buffer range:range];
NSLog(@"buffer %s", buffer);
//[sock readDataToLength:readLength withTimeout:1 tag:MESSAGE_TAG];
} else if (tag == MESSAGE_TAG) {
//[sock readDataToLength:4 withTimeout:1 tag:HEADER_TAG];
}
}
So everything is coming in as one, atomic payload. Perhaps this is because of the way Erlang {packet, 4} works. I hope it is. Otherwise, what's the point of readDataToLength? there's no way to know the length of a message in advance on the client, so what is a good use case to use that method in?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我想这取决于你如何从 Erlang 端发送。选项
{packet, 4}
将发送带有 4 字节长度前缀的每个数据包。 Erlang 中的每个发送操作都会导致发送一个带有其长度前缀的数据包(例如,长度4
的最大大小为 2 Gb)。 Erlang 文档的相关部分适用于 使用inet:setopts/2
设置套接字选项。我猜测数据是迄今为止从套接字读取的总累积数据。如果该数据包含您的整个数据包,那就没问题。但如果没有,您可能需要使用
readDataToLength
继续从套接字对剩余数据进行阻塞读取。It depends on how you send from the Erlang side, I suppose. The option
{packet, 4}
will send each data packet with a 4-byte length prefixed to it. Each send operation in Erlang will result in one packet being sent with it's length prefixed (the max size for length4
, for example, is 2 Gb). The relevant part of the Erlang documentation is for setting the socket options usinginet:setopts/2
.I'm guessing the data is the total accumulated data read from the socket so far. If that data contains your whole packet, it's fine. But if not, you might want to continue to do a blocked read from the socket using
readDataToLength
with the remaining data.