0

I am writing a small app that essentially swaps XML back and forth a-la SOAP. I have an OS X-based server and an iPad client. I use KissXML on the client and the built-in XML parser on the server. I use GCDAsyncSocket on both to communicate.

When I test my app on the iPad simulator, the full XML comes through. Everything works fine.

However, when I use my development device (an actual physical iPad), everything else works fine, but the XML terminates after the 1426th character. I have verified that this error occurs on multiple iPads.

When I subscribe to the incoming packets on GCDAsyncSocket I use [sock readDataWithTimeout:-1 buffer:[NSMutableData new] bufferOffset:0 maxLength:0 tag:0]; and previously just a simple [sock readDataWithTimeout:-1 tag:0]; but both have the same result. It seems that GCDAsyncSocket is not to blame at any rate since the execution is fine on the simulator. Note that the 0 at maxLength indicates an 'infinite' buffer.

Does anyone have any idea what could be causing this?

  • Anything special about character 1426 or any others around it? Also `[NSMutableData new]` will likely leak memory if not using ARC. Consider `[NSMutableData data]`. – Joe Mar 12 '12 at 17:11
  • The data varies from transmission to transmission, but always terminates at the same place, as well as the simulator non-issue; thus my inference of a maximum length somewhere. As a side note, the programs do use ARC. –  Mar 12 '12 at 17:21
  • Depending on your exact network setup, the [MTU](http://en.wikipedia.org/wiki/Maximum_transmission_unit) is often around 1400-1500. Messages longer than that are fragmented and sent in separate packets. When you're running on the simulator, with the server on the same machine, you're not using a physical network, so the effective MTU is much higher and your not getting fragmented. Are you sure you're using `GCDAsyncSocket` correctly? It probably expects that you will call `readDataWithTimeout` repeatedly, not just once. – Kurt Revis Mar 13 '12 at 02:43

3 Answers3

2

1426 sounds very much like the MTU (Maximum Transmit Unit), which is the size of the maximum TCP data you can send. It's different sizes on different network media and different configurations, but 1426 is pretty common.

This suggests that you're confusing the reception of a TCP packet with the completion of an XML message. There is no guarantee that TCP packets will end on an XML message boundary. GCDAsyncSocket is a low-level library that talks TCP, not XML.

As you get each packet, it's your responsibility to concatenate it onto an NSMutableData and then to decide when you have enough to process it. If your protocol closes the connection after every message, then you can read until the connection is closed. If not, then you will have to deal with the fact that a given packet might even include some of the next message. You'll have to parse the data sufficiently to decide where the boundaries are.

BTW, it is very possible that your Mac has a different MTU than your iPad, which is why you may be seeing different behavior on the different platforms.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
1

The solution was that when left unspecified, AsyncSocket looks to the next line-return. When the packet terminates, it indeed returns the line. I was using (sock is my GCDAsyncSocket object)

[sock readDatawithTimeout:-1 tag:0]

but have since moved to

[sock readDataToData:[msgTerm dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0]

where msgTerm is an external constant NSString defined as "\r\n\r\n" and is shared between the client and server source. This effectively circumvents the line return issue ending the packet.

One additional note regarding this solution: Because I am using a SOAP-like protocol, the whitespace is not an issue. However if yours is temperamental about terminating whitespace lines you can use a method like [incomingDecodedNsstringMessage stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] to clean it up.

0

Having had a look at the code for GCDAsyncSocket, I'd say it is entirely possible there is a bug in it. For instance, if you are reading a secure socket, the cfsocket mechanism is used instead of ordinary Unix style file descriptors, on iPhone and the author may be making invalid assumptions about when a socket is closed. Since you have the source code, I'd try stepping through it with a debugger to see if end of file is being flagged prematurely.

TCP is a stream based protocol. Theoretically, the packet size of the underlying IP protocol should make no difference, but if you read the socket fast enough, you may well get your data in chunks the size of the IP packet especially if the IP stack is somehow tuned for memory use (guessing here!).

JeremyP
  • 84,577
  • 15
  • 123
  • 161