Using NSFileHandleConnectionAcceptedNotification for a server process
-
I'm trying to build a simple server and I'm using NSFileHandle to read
the incoming requests. It works, more or less, but what I have run
into is strange. In my call back when I read from the file handle I
always only get the first 510 bytes. I tried to use
int maxBuf=4096;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&maxBuf, sizeof(maxBuf));
before binding and registering for
NSFileHandleConnectionAcceptedNotification but I still only get 510
bytes. I tried other larger and smaller values but the result is
always the same. I could keep queuing up calls with
NSFileHandleReadCompletionNotification but how will I know when I'm
done getting the data I am expecting ? I'm expecting a complete xml
record that I'm going to read process and reply to. I could go back
and retread one written in C but that wouldn't be any fun.
Any ideas how I can get it to feed me more upfront ?
Matt
snippet:
int fd = -1;
CFSocketRef socket;
socket = CFSocketCreate(kCFAllocatorDefault, PF_INET,
SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL);
if( socket ) {
fd = CFSocketGetNative(socket);
int yes = 1;
int maxBuf = 4096;
int err = 0;
err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void
*)&yes, sizeof(yes));
err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void
*)&maxBuf, sizeof(maxBuf));
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
NSData *address4 = [NSData dataWithBytes:&addr4
length:sizeof(addr4)];
if (kCFSocketSuccess != CFSocketSetAddress(socket,
(CFDataRef)address4)) {
NSLog(@"Could not bind to address");
}
} else {
NSLog(@"No server socket");
}
fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd
closeOnDealloc:YES];
NSNotificationCenter *nc = [NSNotificationCenter
defaultCenter];
[nc addObserver:self
selector:@selector(newConnection:)
name:NSFileHandleConnectionAcceptedNotification
object:nil];
[fileHandle acceptConnectionInBackgroundAndNotify];
} -
On 8 Feb '08, at 1:56 PM, Matt Mashyna wrote:> I'm trying to build a simple server and I'm using NSFileHandle to
> read the incoming requests. It works, more or less, but what I have
> run into is strange. In my call back when I read from the file
> handle I always only get the first 510 bytes.
The code you posted accepts new connections, but you didn't show the
code that reads data from a new connection. So I have to guess about
how it works ... one common gotcha is that -readInBackgroundAndNotify:
is a one-shot â it will only notify you once. In that notification
callback, you have to call it again to register for the next bunch of
data. If you don't re-register, the effect is like what you're
reporting.
âJens -
On Feb 8, 2008, at 5:07 PM, Jens Alfke wrote:>
> On 8 Feb '08, at 1:56 PM, Matt Mashyna wrote:
>
>> I'm trying to build a simple server and I'm using NSFileHandle to
>> read the incoming requests. It works, more or less, but what I have
>> run into is strange. In my call back when I read from the file
>> handle I always only get the first 510 bytes.
>
> The code you posted accepts new connections, but you didn't show the
> code that reads data from a new connection. So I have to guess about
> how it works ... one common gotcha is that -
> readInBackgroundAndNotify: is a one-shot â it will only notify you
> once. In that notification callback, you have to call it again to
> register for the next bunch of data. If you don't re-register, the
> effect is like what you're reporting.
>
> âJens
Well, I guess I was stuck on the idea that my problem was really that
I needed to increase the fd buffer so I would get the whole chunk of
data at one time but maybe I'm not thinking about it in the right way.
I can re-register for more data but then it's not clear to me how I
know that the client is done sending it's stream and waiting for a
reply. There doesn't seem to be a NSFileHandle method to see if more
data is waiting to be read. I know I'm being dense. I just need a slap!
Here's where it gets the connect notification:
- (void)newConnection:(NSNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
NSFileHandle *remoteFileHandle = [userInfo objectForKey:
NSFileHandleNotificationFileHandleItem];
NSNumber *errorNo = [userInfo objectForKey:@"NSFileHandleError"];
if( errorNo ) {
NSLog(@"NSFileHandle Error: %@", errorNo);
return;
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dataReceivedNotification:)
name:NSFileHandleReadCompletionNotification
object:remoteFileHandle];
[remoteFileHandle readInBackgroundAndNotify];
} // newConnection
And here's where I actually read it... 510 bytes or less, every time!
- (void)dataReceivedNotification:(NSNotification *)notification
{
NSData *data = [[notification userInfo]
objectForKey:NSFileHandleNotificationDataItem];
NSString* foo = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
NSLog(foo);
[foo release];
// do stuff with the data now
} // dataReceivedNotification -
On 8 Feb '08, at 5:14 PM, Matt Mashyna wrote:> Well, I guess I was stuck on the idea that my problem was really
> that I needed to increase the fd buffer so I would get the whole
> chunk of data at one time but maybe I'm not thinking about it in the
> right way.
I think you could get that effect by calling -
readToEndOfFileInBackgroundAndNotify instead; but I've never tried
that. In generally it's better to read the data in chunks, as that way
you can provide progress feedback to the user. It also avoids loading
all of the data into memory at once.> I can re-register for more data but then it's not clear to me how I
> know that the client is done sending it's stream and waiting for a
> reply. There doesn't seem to be a NSFileHandle method to see if more
> data is waiting to be read. I know I'm being dense. I just need a
> slap!
IIRC, what happens at EOF is that you get a notification whose NSData
payload is either zero bytes long or nil (not sure which). So the
logic is that, if you received at least one byte of data, you call -
readInBackgroundAndNotify again, else you know you're done.
Don't feel bad about being confused about this. The docs are pretty
vague, and I got really confused when I was first trying to use
NSFileHandle too.
âJens


