iOS Reference Library Apple Developer
Search

CFNetServices

CFNetServices is a Core Services API that allows you to register a network service, such as a printer or file server. By registering a service, it can be found by name or browsed for by service type and domain. Applications can use the CFNetServices API to discover the services that are available on the network and to find all access information — such as name, IP address, and port number — needed to use each service.

The CFNetServices API uses the Core Services framework to provide access to Bonjour. It should be used when programming in C or C++ to write applications that need to discover services. If your C or C++ program uses a run loop, use this API.

Requirements

Version 10.2 or later of Mac OS X is required.

Using the CFNetServices API

The CFNetServices API provides access to Bonjour through three objects:

Publishing a Service

Publishing a service on the network involves two tasks: creating a service, and registering a service. The next two sections will describe what is required to perform these two tasks.

Creating a CFNetService

The function that creates a CFNetService (CFNetServiceCreate) requires you to provide the following parameters that describe the service:

Note: The dot in ‚Äúlocal.‚Äú is part of the domain name. It signifies that the domain is fully qualified, which prevents anything from being added to the end of the domain (for example, local.com).

If you are implementing a protocol that relies on data stored in DNS text records, you can associate that information with a CFNetService by calling a separate CFNetServices function (CFNetServiceSetTXTData). Prior to Mac OS X v10.4, use the deprecated function CFNetServiceSetProtocolSpecificInformation instead.

Associate a callback function with your CFNetService by calling CFNetServices function CFNetServiceSetClient . Your callback function will be called to report errors that occur while your service is running.

If you want the service to run asynchronously, you must also schedule the CFNetService on a run loop by calling CFNetServiceScheduleWithRunLoop; otherwise, the service will run synchronously. For more information about the modes in which a service can run, see“Asynchronous and Synchronous Modes.”

Below is some example code for how to create a CFNetService.

Listing 1  Creating a CFNetService

CFNetService netService = CFNetServiceCreate(NULL, CFSTR(""), serviceType, serviceName, chosenPort);

Registering a CFNetService

To make a service available on the network, call the CFNetServices function CFNetServiceRegisterWithOptions that registers a CFNetService. (This process is also known as “publishing” a service.) A CFNetServiceBrowser will be able to find the service until the service is stopped by the CFNetServices function CFNetServiceCancel that terminates services.

Please see Listing 2 for sample code on this subject.

Listing 2  Registering an Asynchronous Service

void startBonjour (CFNetServiceRef netService) {
   CFStreamError error;
   CFNetServiceClientContext clientContext = { 0, NULL, NULL, NULL, NULL };
 
   CFNetServiceSetClient(netService, registerCallback, &clientContext);
   CFNetServiceScheduleWithRunLoop(netService, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
   CFNetServiceRegister(netService, NULL);
   if (CFNetServiceRegister(netService, &error) == false) {
     CFNetServiceUnscheduleFromRunLoop(netService, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
     CFNetServiceSetClient(netService, NULL, NULL);
     CFRelease(netService);
     fprintf(stderr, "could not register Bonjour service");
   }
}

Browsing for Services

To browse for services represented by a CFNetService, call the CFNetServices function that creates CFNetServiceBrowsers, CFNetServiceBrowserCreate. When you create a CFNetServiceBrowser, you need to provide a pointer to your callback function, which will be called when services are found.

If you want searches to be conducted asynchronously, you must also schedule the CFNetServiceBrowser on a run loop withCFNetServiceBrowserScheduleWithRunLoop.

To browse for services you can call the CFNetServices function CFNetServiceBrowserSearchForServices and specify the services to search for. For the domain parameter, you have two options. It is recommended that you pass the empty string (CFSTR("")) as the domain, allowing you to discover services in any domain on which your system is registered. Alternatively, you can specifying a domain to search in. Your callback function will be called and passed a CFNetService representing a matching service. The CFNetServiceBrowser will continue searching until your application stops the search by calling CFNetServiceBrowserStopSearch.

For each CFNetService that your callback function receives, you can call a CFNetServices function CFNetServiceResolveWithTimeout to update the CFNetService with the IP address for the service. Prior to Mac OS X v10.4, use the deprecated function CFNetServiceResolve instead of CFNetServiceResolveWithTimeout. Then call the CFNetService function CFNetServiceGetAddressing to get a CFArray containing a CFDataRef for each IP address associated with the service. Each CFDataRef consists of a sockaddr structure containing an IP address.

A good example of how to browse for services can be seen in Listing 3.

Listing 3  Browsing Asynchronously for Services

static Boolean MyStartBrowsingForServices(CFStringRef type, CFStringRef domain) {
     CFNetServiceClientContext clientContext = { 0, NULL, NULL, NULL, NULL };
     CFStreamError error;
     Boolean result;
 
     assert(type != NULL);
 
     gServiceBrowserRef = CFNetServiceBrowserCreate(kCFAllocatorDefault, MyBrowseCallBack, &clientContext);
     assert(gServiceBrowserRef != NULL);
 
     CFNetServiceBrowserScheduleWithRunLoop(gServiceBrowserRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
 
     result = CFNetServiceBrowserSearchForServices(gServiceBrowserRef, domain, type, &error);
     if (result == false) {
 
         // Something went wrong so lets clean up.
         CFNetServiceBrowserUnscheduleFromRunLoop(gServiceBrowserRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);         CFRelease(gServiceBrowserRef);
         gServiceBrowserRef = NULL;
 
         fprintf(stderr, "CFNetServiceBrowserSearchForServices returned (domain = %d, error = %ld)\n", error.domain, error.error);
     }
 
     return result;
}

Resolving a Service

Once you have a name, type and domain, you can resolve the service to retrieve its host name and port. Like registering a service, resolving a service also needs to create a CFNetService reference by calling CFNetServiceCreate. Beginning in Mac OS X v10.4, When you call CFNetServiceCreate it is important to pass a valid domain that was obtained when the service was first detected. Starting with Mac OS X v10.4, if you pass an empty CFString for the domain argument, Bonjour will not equate it with the domain local. as it did in releases of Mac OS X prior to v10.4.

If you plan to resolve a service asynchronously, you should then associate the newly created CFNetServiceRef with a callback function, which will receive a CFNetServiceRef, and a pointer to a CFStreamError. The callback association is performed by calling CFNetServiceSetClient and following it with CFNetServiceScheduleWithRunLoop to add the service to a run loop. To use the current run loop, pass CFRunLoopGetCurrent() as a parameter.

After setting up the run loop, call the function CFNetServiceResolve, ensuring that it does not return an error. If an error is returned, then you should clean up all the references you created. Otherwise just wait for your callback functions to be called.

An example of resolving a service with CFNetService is in Listing 4.

Listing 4  Resolving a Service Asynchronously

static void MyResolveService(CFStringRef name, CFStringRef type, CFStringRef domain)
{
    CFNetServiceClientContext context = { 0, NULL, NULL, NULL, NULL };
    CFTimeInterval duration = 0; // use infinite timeout
    CFStreamError error;
 
    gServiceBeingResolved = CFNetServiceCreate(kCFAllocatorDefault, domain, type, name, 0);
    assert(gServiceBeingResolved != NULL);
 
    CFNetServiceSetClient(gServiceBeingResolved, MyResolveCallback, &context);
    CFNetServiceScheduleWithRunLoop(gServiceBeingResolved, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
 
    if (CFNetServiceResolveWithTimeout(gServiceBeingResolved, duration, &error) == false) {
 
         // Something went wrong so lets clean up.
         CFNetServiceUnscheduleFromRunLoop(gServiceBeingResolved, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
         CFNetServiceSetClient(gServiceBeingResolved, NULL, NULL);
         CFRelease(gServiceBeingResolved);
         gServiceBeingResolved = NULL;
 
         fprintf(stderr, "CFNetServiceResolve returned (domain = %d, error = %ld)\n", error.domain, error.error);
}
 
    return;
}

Monitoring a Service

CFNetServiceMonitor, which debuted in Mac OS X v10.4, gives developers the ability to watch services for changes to their TXT records. In order to monitor a service asynchronously, you need to follow the same general formula that is used for resolving a service.

Once you have a CFNetServiceRef for a service, you need to create a monitor reference (CFNetServiceMonitorRef using the function CFNetServiceMonitorCreate. Like resolving a service, the next step is to schedule the monitor reference on a run loop with CFNetServiceMonitorScheduleWithRunLoop. The default run loop can be obtained by calling CFRunLoopGetCurrent().

After the monitor reference has been added to a run loop, you can start the monitor with the function CFNetServiceMonitorStart passing it the monitor reference. Make sure to check the return value to ensure that the monitor has started.

When you have finished monitoring a service, call CFNetServiceMonitorStop to stop the monitoring, CFNetServiceMonitorUnscheduleFromRunLoop to unschedule your monitor from its run loop and CFNetServiceMonitorInvalidate to destroy the monitor reference.

Asynchronous and Synchronous Modes

Several CFNetServices functions can operate in asynchronous or synchronous mode. Scheduling a CFNetService or a CFNetServiceBrowser on a run loop causes the service or browser to operate in asynchronous mode. If a CFNetService or a CFNetServiceBrowser is not scheduled on a run loop, it operates in synchronous mode. Operating in asynchronous mode changes the behavior of functions.

While it is possible to use the synchronous modes of these functions, please keep in mind that it is unacceptable to block the user interface or other functions of your program while you wait for synchronous functions to return. Due to the arbitrary amount of time network operations may last, it is highly recommended that you use the asynchronous modes of each function.

Table 1  Behavior of certain CFNetServices functions in asynchronous and synchronous mode

Function

Asynchronous mode

Synchronous mode

CFNetServiceRegisterWithTimeout (Mac OS X v10.4 only)

Starts the registration and returns. The callback function for the CFNetService will be called to report any errors that occur while the service is running. The service is available on the network until your application cancels the registration.

Blocks until your application cancels the service from another thread or until an error occurs, at which point the function returns. The error is returned in an error structure pointed to by a parameter of the CFNetServiceRegisterWithOptions function. The service is available on the network until your application cancels the registration or an error occurs.

CFNetServiceResolveWithTimeout (Mac OS X v10.4 only)

Starts the resolution and returns. The callback function for the CFNetService will be called to report any errors that occur during resolution. The resolution process runs until the specified timeout is reached, or, if the timeout was specified as zero, until it is canceled.

Blocks until at least one IP address is found for the service, an error occurs, the time specified as the timeout parameter is reached or your application cancels the resolution, at which point the function returns. If an error occurs, the error is returned in an error structure pointed to by a parameter to the CFNetServiceResolveWithTimeout function. The resolution process continues to run until your application cancels it or an error occurs.

CFNetServiceBrowserSearchForDomains

Starts the search and returns. The callback function for the CFNetServiceBrowser will be called for each domain that is found and to report any errors that occur while browsing. Browsing continues to run until your application stops the browsing.

Blocks until an error occurs or your application calls CFNetServiceBrowserStopSearch at which time, the callback function for the CFNetServiceBrowser will be called for each domain that was found. Any error is returned in an error structure pointed to by a parameter to the CFNetServiceBrowserSearchForDomains function. Browsing continues until your application stops the browsing.

CFNetServiceBrowserSearchForServices

Starts the search and returns. The callback function for the CFNetServiceBrowser will be called for each CFNetService that is found and to report any errors that occur while browsing. Browsing continues to run until your application stops the browsing.

Blocks until an error occurs or until your application calls CFNetServiceBrowserStopSearch at which time, the callback function for the CFNetServiceBrowser will be called for each CFNetService that was found. Any error is returned in an error structure pointed to by a parameter to the CFNetServiceBrowserSearchForServices function. Browsing continues until your application stops the browsing.

Shutting Down Services and Searches

To shut down a service that is running in asynchronous mode, your application unschedules the service from all run loops it may be scheduled on and then calls CFNetServiceSetClient with the clientCB parameter set to NULL to disassociate your callback function from the CFNetService. Then call CFNetServiceCancel to stop the service. If the service is running in synchronous mode, you only need to call CFNetServiceCancel from another thread.

Listing 5 shows a good example of how to shut down an asynchronous CFNetService Resolve process which has not timed out.

Listing 5  Canceling an Asynchronous CFNetService Resolve Process

void MyCancelResolve()
{
     assert(gServiceBeingResolved != NULL);
     CFNetServiceUnscheduleFromRunLoop(gServiceBeingResolved, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
     CFNetServiceSetClient(gServiceBeingResolved, NULL, NULL);
     CFNetServiceCancel(gServiceBeingResolved);
     CFRelease(gServiceBeingResolved);
     gServiceBeingResolved = NULL;
     return;
}

To shut down a browser that is running in asynchronous mode, your application unschedules the browser from all run loops it may be scheduled on and then calls CFNetServiceBrowserInvalidate. Then your application calls CFNetServiceBrowserStopSearch. If the browser is running in synchronous mode, you only need to call CFNetServiceBrowserStopSearch. An example of these functions can be seen in Listing 6.

Listing 6  Stop Browsing for Services

static void MyStopBrowsingForServices()
{
     assert(gServiceBrowserRef != NULL);
     CFNetServiceBrowserStopSearch(gServiceBrowserRef, &streamerror);
     CFNetServiceBrowserUnscheduleFromRunLoop(gServiceBrowserRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
     CFNetServiceBrowserInvalidate(gServiceBrowserRef);
     CFRelease(gServiceBrowserRef);
     gServiceBrowserRef = NULL;
     return;
}



Last updated: 2010-03-24

Did this document help you? Yes It's good, but... Not helpful...