NSFileHandle
provides methods that let you read and
write data from a file or communication channel asynchronously in the
background. Chapter 2 discussed file access with
NSFileHandle
. This section describes
NSFileHandle
’s asynchronous
communications features and how they apply to networking.
You can obtain a socket file descriptor from an instance of
NSSocketPort
by sending a
socket
message to the socket port object. With the
socket descriptor, you are able to initialize an instance of
NSFileHandle
with the method
initWithFileDescriptor
:. The
initWithFileDescriptor:closeOnDealloc
: method is
an extension of this method that specifies whether or not the file
descriptor will close when the file handle object is deallocated. By
default, the file handle object does not close the file descriptor
and ownership of that descriptor remains with the object that created
the file handle. To determine an NSFileHandle
instance’s file descriptor, invoke the method
fileDescriptor
.
NSFileHandle
provides
the following three methods for performing asynchronous background
communication:
acceptConnectionInBackgroundAndNotify
This method is valid only for NSFileHandle
instances initialized with a socket file descriptor (of type
SOCK_STREAM
), and causes the socket represented by
the file handle to listen for new connections. This method returns
immediately while a background thread accepts client connections over
the socket. Observers are notified of new connections by registering
for the notification
NSFileHandleAcceptedConnectionNotification
. The
notification’s userInfo
dictionary contains a new socket file handle connected to the client
that initiated the connection, which frees the listening socket to
accept additional connections. You can obtain the socket from the
userInfo
dictionary through the key
NSFileHandleNotificationFileHandleItem
.
readInBackgroundAndNotify
This method begins an asynchronous read operation on a socket in the
background by invoking the method availableData
.
This method is generally invoked in the file handle object that
represents the socket connected to the client that is obtained from
the NSFileHandleAcceptedConnectionNotification
userInfo
dictionary. When data is read, the
connected socket file handle posts an
NSFileHandleReadCompletionNotification
, whose
userInfo
dictionary contains the data that was
read. You can obtain this NSData
object by using
the NSFileHandleNotificationDataItem
key.
readToEndOfFileInBackgroundAndNotify
This method behaves similarly to
readInBackgroundAndNotify
, except the
NSFileHandle
method
readToEndOfFile
is invoked. When all data has
finished being read, the file handle posts an
NSFileHandleReadToEndOfFileCompletionNotification
notification. You can obtain the read data from the
userInfo
dictionary by using the
NSFileHandleNotificationDataItem
key.
NSFileHandle
declares three additional methods
that include a ForModes
: argument:
acceptConnectionInBackgroundAndNotifyForModes
:
readInBackgroundAndNotifyForModes
:
readToEndOfFileInBackgroundAndNotifyForModes
:
The ForModes
: argument specifies which run loop
modes each method’s notification may be posted in.
This specification provides finer control over operation of the
notification process.
Example 6-8 shows how to use
NSFileHandle
to implement a simple server
infrastructure.
Example 6-8. Using NSFileHandle for server socket communication
- (void)startServer { NSSocketPort *sockPort; sockPort = [[NSSocketPort alloc] initWithTCPPort:12345]; int socketFD = [sockPort socket]; NSFileHandle *listeningSocket; listeningSocket = [[NSFileHandle alloc] initWithFileDescriptor:socketFD]; NSNotificationCenter *nc; nc = [NSNotificationCenter defaultNotificationCenter]; [nc addObserver:self selector:@selector(spawnChildConnection:) name:NSFileHandleConnectionAcceptedNotification object:listeningSocket]; [listeningSocket acceptConnectionInBackgroundAndNotify]; }/*
* This method is invoked in response to
* NSFileHandleConnectionAcceptedNotification
*/
- (void)spawnChildConnection:(NSNotification *)note { NSFileHandle *connectedSocket = [[note userInfo] objectForKey:NSFileHandleNotificationFileHandleItem]; NSNotificationCenter *nc; nc = [NSNotificationCenter defaultNotificationCenter]; [nc addObserver:self selector:@selector(processClientData:) name:NSFileHandleReadCompletionNotification object:connectedSocket];// Send a message to the client, acknowledging that the connection was accepted
[connectedSocket writeData:ackData]; [connectedSocket readInBackgroundAndNotify]; }/*
* This method is invoked in response to
* NSFileHandleReadCompletionNotification
*/
- (void)processClientData:(NSNotification *)note { NSData *data = [[note userInfo] objectForKey:NSFileHandleNotificationDataItem];// Do something here with your data
// Tell file handle to continue waiting for data
[[note object] readInBackgroundAndNotify]; }
Example 6-9 shows a client infrastructure using
NSFileHandle
.
Example 6-9. Using NSFileHandle on the client side
- (void)startClient { NSSocketPort *sockPort; sockPort = [[NSSocketPort alloc] initRemoteWithTCPPort:12345 host:@"10.0.1.3"]; int sockFD = [sockPort socket]; NSFileHandle *clientSocket; clientSocket = [[NSFileHandle alloc] initWithFileDescriptor:sockFD]; NSNotificationCenter *nc; nc = [NSNotificationCenter defaultNotificationCenter]; [nc addObserver:self selector:@selector(processServerData:) name: NSFileHandleReadCompletionNotification object: clientSocket]; [clientSocket writeData:dataToWrite]; [clientSocket readInBackgroundAndNotify]; }/*
* This method is invoked in response to
* NSFileHandleReadCompletionNotification
*/
- (void) processServerData:(NSNotification *)note { NSData *data = [[note userInfo] objectForKey:NSFileHandleNotificationDataItem];// Do something here with your data
// Tell file handle to continue waiting for data
[[note object] readInBackgroundAndNotify]; }