realtime streaming

54 views
Skip to first unread message

hurricane_...@yahoo.com

unread,
Mar 17, 2009, 11:36:23 AM3/17/09
to CocoaAsyncSocket
I'm trying to use the TCP protocol to send data more like UDP, in that
I want to send short messages like "SEND,a,b" and have them arrive on
the server end as they are generated, instead of accumulating multiple
messages and then sending them all at once like "SEND,a,b|SEND,e,s|
SEND,q,w|...". I was using UDP before and it worked perfectly, but
now I have to switch to TCP and it's sending it over as chunks of
commands, instead of line by line. Other programs like mine do this
just fine using TCP, so I know it's possible, just don't know how to
configure this to do it.
Any suggestions?

Robert Hanson

unread,
Mar 17, 2009, 12:51:44 PM3/17/09
to cocoaasy...@googlegroups.com
You can use TCP with a "UDP mindset", you only need to know how to separate the messages on arrival.  Luckily AsyncSocket comes with several tools to help with this.

Internally, TCP knows nothing about the messages you're sending.  TCP just thinks of all the data it sends/receives as one big long stream.  So it's our job to separate the messages once they arrive.  The best way to do this depends on the format of the messages.

If every message sent is an exact length (all messages are X bytes), then you could simply do something like this:
[asyncSocket readDataToLength:X timeout:-1 tag:0];

Every call like this would return exactly one full message.

Or perhaps every message has a terminating CRLF ("\r\n") similar to protocols like HTTP, POP3, etc.  Then you could do something like this:
[asyncSocket readDataToData:[AsyncSocket CRLFData] timeout:-1 tag:0];

If your messages don't have a fixed length, and they don't have a set terminator, then you could do something like prefix each message with a UInt16 length field:

- (void)start
{
    [asyncSocket readDataToLength:2 timeout:-1 tag:LENGTH_TAG];
}

- (void)onSocket:(AsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
    if(tag == LENGTH_TAG)
    {
        UInt16 length = [self extractUInt16FromData:data atOffset:0 andConvertFromNetworkOrder:YES];
        [asyncSocket readDataToLength:length timeout:-1 tag:BODY_TAG];
    }
    else
    {
        // data is the message
    }
}

- (UInt16)extractUInt16FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag
{
// 16 bits = 2 bytes
if([data length] < offset + 2) return 0;
UInt16 *pResult = (UInt16 *)([data bytes] + offset);
UInt16 result = *pResult;
if(flag)
return ntohs(result);
else
return result;
}

-Robbie Hanson
-Deusty Designs


hurricane_...@yahoo.com

unread,
Mar 17, 2009, 1:00:21 PM3/17/09
to CocoaAsyncSocket
So is all the work done on the server end? I was guessing that the
client was buffering the data and then sending it in groups rather
than line by line, but are you saying that the client is sending the
data as it's being written and the server is the one who controls how
quick it gets read?

Robert Hanson

unread,
Mar 17, 2009, 2:49:42 PM3/17/09
to cocoaasy...@googlegroups.com
I think, perhaps, you're a bit confused about what TCP is, and how it
works.

UDP has a concept of datagrams (packets). You send a packet, and the
packet arrives on the other side. You send 3 packets, and 3 separate
packets arrive on the other side.

None of this is true for TCP. TCP has absolutely no concept of
datagrams.

TCP deals with only a single stream of data. TCP considers every
single write call to be a smaller part of the larger stream.
Therefore, it will combine your writes to maximize the data
transmission rate.

For example, say you do this:

socket.write(msg1); // msg1 is 10 bytes
socket.write(msg2); // msg2 is 20 bytes
socket.write(msg3); // msg3 is 50 bytes

(Notice this example was written in some other language like C. This
is because the example is true of TCP regardless of which language
you're writing your application in.)

It is VERY likely that TCP will send a single "packet" that contains
all 3 messages. So the other side will receive all 3 messages at
exactly the same time.

-Robbie Hanson
-Deusty Designs

hurricane_...@yahoo.com

unread,
Mar 17, 2009, 4:50:54 PM3/17/09
to CocoaAsyncSocket
I understand that. My question is whether it is possible to prevent
the client from trying to optimize the traffic by accumulating enough
data before it sends it? I've used other languages that allow you to
set a property to turn this off and have the data sent out as soon as
it's written to the socket, I believe it's called the Nagle algorithm?
So although it wouldn't necessarily be sending the data in the same
chucks as they are written, like UDP, it would be sending out data as
soon as it's written. So you may get some overlap between messages,
but overall the data being received would come in as fast as the
client was sending, instead of the client buffering enough data before
sending it. Is there a way to do that with this class? I've done it
with other languages.
Thanks for your help!


On Mar 17, 2:49 pm, Robert Hanson <robbiehan...@deusty.com> wrote:
> I think, perhaps, you're a bit confused about what TCP is, and how it  
> works.
>
> UDP has a concept of datagrams (packets).  You send a packet, and the  
> packet arrives on the other side.  You send 3 packets, and 3 separate  
> packets arrive on the other side.
>
> None of this is true for TCP.  TCP has absolutely no concept of  
> datagrams.
>
> TCP deals with only a single stream of data.  TCP considers every  
> single write call to be a smaller part of the larger stream.  
> Therefore, it will combine your writes to maximize the data  
> transmission rate.
>
> For example, say you do this:
>
> socket.write(msg1); // msg1 is 10 bytes
> socket.write(msg2); // msg2 is 20 bytes
> socket.write(msg3); // msg3 is 50 bytes
>
> (Notice this example was written in some other language like C.  This  
> is because the example is true of TCP regardless of which language  
> you're writing your application in.)
>
> It is VERY likely that TCP will send a single "packet" that contains  
> all 3 messages.  So the other side will receive all 3 messages at  
> exactly the same time.
>
> -Robbie Hanson
> -Deusty Designs
>

Robert Hanson

unread,
Mar 17, 2009, 5:28:54 PM3/17/09
to cocoaasy...@googlegroups.com
Yes, you can do it like so:

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(@"onSocket:%p didConnectToHost:%@ port:%hu", sock, host, port);
CFSocketRef cfsock = [asyncSocket getCFSocket];
CFSocketNativeHandle rawsock = CFSocketGetNative(cfsock);
int flag = 1;
int result = setsockopt(rawsock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
if (result != 0)
{
NSLog(@"Couldn't disable nagle...");
}

// ...
}

Some useful links:

hurricane_...@yahoo.com

unread,
Mar 18, 2009, 12:47:26 PM3/18/09
to CocoaAsyncSocket
Awesome, exactly what I was looking for! Works even better than UDP
for my purpose.
Thanks so much!!!!

On Mar 17, 5:28 pm, Robert Hanson <robbiehan...@deusty.com> wrote:
> Yes, you can do it like so:
>
> - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host  
> port:(UInt16)port
> {
>         NSLog(@"onSocket:%p didConnectToHost:%@ port:%hu", sock, host, port);
>
>         CFSocketRef cfsock = [asyncSocket getCFSocket];
>         CFSocketNativeHandle rawsock = CFSocketGetNative(cfsock);
>
>         int flag = 1;
>         int result = setsockopt(rawsock, IPPROTO_TCP, TCP_NODELAY, (char  
> *)&flag, sizeof(int));
>
>         if (result != 0)
>         {
>                 NSLog(@"Couldn't disable nagle...");
>         }
>
>         // ...
>
> }
>
> Some useful links:http://lists.apple.com/archives/macnetworkprog/2003/May/msg00064.htmlhttp://www.unixguide.net/network/socketfaq/2.16.shtml
>
> -Robbie Hanson
> -Deusty Designs
Reply all
Reply to author
Forward
0 new messages