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
NSNetService
instance with name, type, domain, and port number, and assign a delegate to the object.Publish the
NSNetService
instance.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:
For TCP, use a
CFSocketRef
object to listen for incoming connections, then useNSStream
orCFStreamRef
object 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
CFSocketRef
object 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
searchForRegistrationDomains
method (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
NSNetService
object. This example uses the default domain(s) for publication and a hypothetical TCP/IP music service.Sets the delegate for the
NSNetService
object. This object handles all results from theNSNetService
object, 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