6.5 NSFileHandle

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];
}


    Part II: API Quick Reference