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:
Set up a valid TCP listening socket (or a UDP data socket) for communication.
Initialize an
NSNetServiceinstance with name, type, domain, and port number, and assign a delegate to the object.Publish the
NSNetServiceinstance.Respond to messages sent to the
NSNetServiceobject’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:
For TCP, use a
CFSocketRefobject to listen for incoming connections, then useNSStreamorCFStreamRefobject for the actual communication. For good examples of how to set up a network listener, see the RemoteCurrency, CocoaEcho, and SimpleNetworkStreams sample code projects.For UDP, use a
CFSocketRefobject for sending and receiving packets. For an example, see the UDPEcho sample code project.
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:
@""—Registers the service in the default set of domains. Pass this value unless you have a specific reason not to.@"local"—Registers the service only on the local network. Pass this value if you need to prevent publishing your service over Back to My Mac or wide-area Bonjour.A user-specified domain—Registers the service in only the specified domain. To retrieve a list of existing domains, call the
searchForRegistrationDomainsmethod (as described in Browsing for Domains) or allow the user to enter an arbitrary domain name.
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:
Initializes the
NSNetServiceobject. This example uses the default domain(s) for publication and a hypothetical TCP/IP music service.Sets the delegate for the
NSNetServiceobject. This object handles all results from theNSNetServiceobject, as described in Implementing Delegate Methods for Publication.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:
netServiceWillPublish:netServiceDidPublish:netService:didNotPublish:netServiceDidStop:
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.
Copyright © 2013 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2013-08-08