Publishing Network Services

Bonjour enables dynamic discovery of network services on IP networks without a centralized directory server. The Foundation framework’s NSNetService class represents instances of Bonjour network services. This chapter describes the process for publishing Bonjour network services with NSNetService.

The Publication Process

Bonjour network services use standard DNS information to advertise their existence to potential clients on a network. In Cocoa, the NSNetService class handles the details of service publication.

Typically, you use NSNetService to publish a service provided by a socket owned by the same process. However, because the NSNetService class does not use the socket in any way, you can also use the class to advertise on behalf of another process’s service, such as an FTP server process that has not yet been updated to support Bonjour. However, if you are creating an IP network service, you should include Bonjour publication code as part of its startup process.

Because network activity can sometimes take some time, NSNetService objects process publication requests asynchronously, delivering information through delegate methods. To use NSNetService correctly, your app must assign a delegate to each NSNetService instance it creates. Because the identity of the NSNetService object is passed as a parameter in delegate methods, you can use one delegate for multiple NSNetService objects.

Publishing a Bonjour network service takes four steps:

  1. Set up a valid TCP listening socket (or a UDP data socket) for communication.

  2. Initialize an NSNetService instance with name, type, domain, and port number, and assign a delegate to the object.

  3. Publish the NSNetService instance.

  4. Respond to messages sent to the NSNetService object’s delegate.

The following sections describe these steps in detail.

Configuring a Socket for Your Service

Bonjour network services require either a TCP listening socket or a UDP data socket.

If you already have existing networking code, you can continue using it as is, and provide its port number to Bonjour when you initialize the service.

If you don’t have existing networking code, use the CFSocket API:

To learn why CFSocket is recommended, read Using Sockets and Socket Streams in Networking Overview.

For a more detailed overview of how to write a TCP- or UDP-based daemon, read Using Sockets and Socket Streams in Networking Programming Topics.

Initializing and Publishing a Network Service

To initialize an NSNetService instance for publication, use the initWithDomain:type:name:port: method. This method sets up the instance with appropriate socket information and adds it to the current run loop.

The service type expresses both the application-layer protocol (FTP, HTTP, and so on) and the transport protocol (TCP or UDP). The format is as described in Domain Naming Conventions, for example, _printer._tcp for a printer over TCP.

The service name can be an arbitrary NSString, but the value must be no longer than 63 bytes in UTF-8 encoding. Because this is the name that should be presented to users, it should be human-readable and descriptive of the specific service instance. Consider letting the user override any default name that you provide.

One recommended approach is to use the computer name as the service name. If you pass the empty string (@"") for the service name parameter, the system automatically advertises your service using the computer name as the service name. For examples of other naming approaches, read Bonjour Overview.

If you need to construct the service name in a nonstandard way, you can retrieve the computer name yourself. In OS X on the desktop, you can obtain the computer name by calling the SCDynamicStoreCopyComputerName function from the System Configuration framework. In iOS, you can obtain the same information from the name property of the UIDevice class.

When publishing a service, you must also specify the domain in which the service should be published. Here are some common values:

Upon initialization, the NSNetService object is automatically scheduled on the current run loop with the default mode. If you want to schedule it on a different run loop or with a different mode, you can call the removeFromRunLoop:forMode: and scheduleInRunLoop:forMode: methods.

After the initialization is complete and valid, assign a delegate to the NSNetService object with the setDelegate: method. Finally, publish the service with the publish method, which returns immediately. Bonjour performs the publication asynchronously and returns results through delegate methods.

Listing 2-1 demonstrates the initialization and publication process for Bonjour network services. An explanation of the code follows it. For a good example of service publication, see the PictureSharing sample code project in the Mac Developer Library.

Listing 2-1  Initializing and publishing a Bonjour network service

void myRegistrationFunction(uint16_t port) {
id delegateObject;      // Assume this exists.
NSNetService *service;
 
    service = [[NSNetService alloc] initWithDomain:@""// 1
                                    type:@"_music._tcp"
                                    name:@""
                                    port:port];
    if(service)
    {
        [service setDelegate:delegateObject];// 2
        [service publish];// 3
    }
    else
    {
        NSLog(@"An error occurred initializing the NSNetService object.");
    }
}

Here’s what the code does:

  1. Initializes the NSNetService object. This example uses the default domain(s) for publication and a hypothetical TCP/IP music service.

  2. Sets the delegate for the NSNetService object. This object handles all results from the NSNetService object, as described in Implementing Delegate Methods for Publication.

  3. Publishes the service to the network.

To stop a service that is already running or is in the process of starting up, use the stop method.

Implementing Delegate Methods for Publication

NSNetService provides your app with publication status information by calling methods on its delegate. If you are publishing a service, your delegate object should implement the following methods:

The netServiceWillPublish: method notifies the delegate that Bonjour is ready to publish the service. When this method is called, the service is not yet visible to the network, which means that publication may still fail. However, you can assume that the service is visible unless NSNetService calls your delegate’s netService:didNotPublish: method.

The netServiceDidPublish: method notifies the delegate that Bonjour has successfully published the service.

The netService:didNotPublish: method is called when publication fails for any reason. Your delegate's netService:didNotPublish: method should extract the type of error from the returned dictionary using the NSNetServicesErrorCode key and handle the error accordingly. For a complete list of possible errors, see NSNetService Class Reference.

The netServiceDidStop: method gets called as a result of the stop message being sent to the NSNetService object. If this method gets called, the service is no longer published.