Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CocoaAsyncSocket and reading data from a socket

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?

like image 615
randombits Avatar asked Nov 14 '22 19:11

randombits


1 Answers

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 length 4, for example, is 2 Gb). The relevant part of the Erlang documentation is for setting the socket options using inet: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.

like image 193
Adam Lindberg Avatar answered Dec 21 '22 06:12

Adam Lindberg