[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Q] NSFileHandle -readDataOfLength behaviour
From: |
Richard Frith-Macdonald |
Subject: |
Re: [Q] NSFileHandle -readDataOfLength behaviour |
Date: |
Sat, 14 Apr 2007 07:26:02 +0100 |
On 13 Apr 2007, at 20:03, Vaisburd, Haim wrote:
Richard Frith-Macdonald [mailto:richard@tiptree.demon.co.uk] wrote:
Tima Vaisburd wrote:
RFM> this method should block until it reads the specified amount of
data
RFM> (or eof).
but for my stream class I need exactly what read() does, since the
whole idea was to read byte after byte until the record ends (which
is
determined by the structure of the incoming byte stream, not by the
length passed in the header).
Maybe availableData is what you want.
-availableData does not have a limit. I might be able to use it,
but what I would really want is -readDataOfLength that would not block
until it gets all the length but would return what's available,
blocking only until the first byte comes ( i.e. like read() ).
I think you need to use NSStream for that (or resort to using the read
() system call).
I hoped that that's what it should do and was a little disappointed
when you said "block". Since I cannot test on Mac right now (I have
one
recently but haven't programmed on it yet) I searched the web for
the proof. Could not find one but I found two other open source
implementations that do what I want and not what you say (see below).
Of course they are not a proof of anything, but raise a concern in my
mind.
Could you, please, test the behavior on Mac OSX or just confirm that
you've
done it before?
I just tested again to make sure ...
The test I did was to connect to an smtp server ... which is great
because, upon connection the server writes an introductory message
and then waits for input, so if your client just tried to read a
buffer bigger than the introductory message you get to check the
behavior you are interested in.
I modified your test program to do the job ....
#import <Foundation/Foundation.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
void fatal( const char * s )
{
char buf[40];
sprintf( buf, "%s", s );
perror( buf );
exit(1);
}
int connectToSocket( const char * addr, int port )
{
int socket_fd = socket( AF_INET, SOCK_STREAM, PF_UNSPEC );
if ( socket_fd == -1 )
fatal( "socket" );
struct sockaddr_in sa;
memset( &sa, 0, sizeof(sa) );
sa.sin_family = AF_INET;
inet_aton(addr, &sa.sin_addr);
sa.sin_port = htons(port);
if ( connect( socket_fd, (struct sockaddr *)&sa, sizeof( sa ) ) == -1
)
fatal( "connect" );
return socket_fd;
}
int main(int argc, char ** argv )
{
[NSAutoreleasePool new];
NSLog( @"Connecting to socket");
int fd = connectToSocket( "192.168.66.9", 25 );
NSFileHandle * fh = [[NSFileHandle alloc] initWithFileDescriptor:
fd];
NSData * resp = [fh readDataOfLength: 1024];
// NSData * resp = [fh availableData];
NSString * response = [[NSString alloc] initWithData: resp
encoding:
NSASCIIStringEncoding];
NSLog( @"got response: \"%@\"", response );
return 0;
}
As a sanity check I used the netcat (nc) program to read from the
smtp port too.
With -readDataOfLength: the test program just blocked producing no
output, with -availableData it printed out the smtp server's intro
(which is also what netcat did).
Then I went on to the smtp server machine, found the process which
was handling the incoming connection from the test program, and
killed it. At that point the test program displayed the intro text
(because it got an eof when the remote process went away).
So I think the gnustep-base implementation matches the MacOS-X and
the other two you found are 'wrong' in the sense that they don't
match MacOS behavior (though the documentation is vague enough that
either implementation could be viewed as correct).
If you really want to read an individual byte at a time, NSStream is
probably better than NSFileHandle ...
but that's a shockingly inefficient way to do I/O
Do you refer to NSStream or "byte at a time" as being shockingly
inefficient?
Byte at a time.
Of course I meant "byte at a time" in the way getc() macro of standard
IO library does it:
read a block with a system call, scan it with something fast.
That's not what I'd call byte at a time I/O ... getc() uses buffered
I/O ... large blocks of data are read and then bytes returned from a
buffer. This is not what NSFileHandle does. NSFileHandle reads a
block into a buffer and returns the entire buffer ... so if you used
readDataOfLength: with a length of 1 (for byte at a time) then each
call would allocate a new data object and read a single byte from the
operating system ... horribly inefficient.
I guess what you want is something more like this ...
- (int) getByte
{
if (offset == length)
{
[data release];
data = [[handle availableData] retain];
ptr = [data bytes];
length = [data length];
offset = 0;
if (offset == length)
{
return -1;
}
}
return ptr[offset++];
}