Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Networking With Open Transport / Part 1 - Open Transport Essentials
Chapter 6 - Mappers


Using Mappers

This section begins by describing how the general provider functions that govern a provider's mode of operation apply to mapper providers. It goes on to discuss information you need to know in order to use mapper functions: how you format names and addresses specified in parameters to mapper functions and how you handle processing when calling mapper functions asynchronously. This section concludes with a discussion of different techniques you can use when using the mapper to search for a name pattern.

Setting Modes of Operation for Mappers

Like all Open Transport providers, mappers can use different modes of operation. A mapper can execute synchronously or asynchronously. You set the mapper's default mode of execution by using the appropriate function to open it; for example, you can create a mapper that executes asynchronously by calling the OTAsyncOpenMapper function. After opening the mapper, you can change its mode of execution by calling the OTSetSynchronous or OTSetAsynchronous functions. To determine how mapper functions execute, you call the OTIsSynchronous function.

Mappers use one asynchronous event and four completion events. Table 6-1 lists the event codes that the mapper provider can pass to your application and explains the meaning of the cookie parameter to the notifier for each function. For more detailed information, see the descriptions of the mapper functions in "Functions".

Table 6-1 Completion events for asynchronous mapper functions
Completion codeMeaning
T_OPENCOMPLETEThe OTAsyncOpenMapper function has completed. The cookie parameter contains the mapper reference.
T_REGNAMECOMPLETEThe OTRegisterName function has completed. The cookie parameter contains the reply parameter, unless it was NULL, in which case it contains the request parameter.
T_DELNAMECOMPLETEThe OTDeleteName or the OTDeleteNameByID functions have completed. For the OTDeleteName function, the cookie parameter holds a pointer to the name parameter. For the OTDeleteNameByID function, the cookie parameter contains the id parameter.
T_LKUPNAMERESULTThe OTLookupName function has returned a name, but it has not yet completed because there might be more names to retrieve.
T_LKUPNAMECOMPLETE  The OTLookupName function has completed. The cookie parameter contains the reply parameter.

The only way to cancel an asynchronous mapper function is to call the OTCloseProvider function, passing the mapper reference for which the function was executed. The OTCloseProvider function is described in the chapter "Providers" in this book.

By default, mappers do not block and do not acknowledge sends. You can change a mapper's blocking status by using the OTSetBlocking function. Mapper providers are not affected by their send-acknowledgment status. However, a mapper provider's blocking status might affect the behavior of mapper functions. For example, if a mapper is blocking, heavy network traffic might cause mapper functions to wait before sending or receiving data. If a mapper is nonblocking and you are doing a lot of name lookups, the OTLookupName function might return with the kOTFlowErr result. In this case, you can try executing the function later.

Specifying Name and Address Information

Several mapper functions require that you specify a name or address. This might be a name to register or to look up. Specifying a name or address means that you have to create a buffer that contains the information and then create a TNetbuf structure that specifies the size and location of this buffer. The format that you use to store a name or an address is specific to the name-registration protocol that underlies the mapper and is exactly the same as the name and address formats that you can use to bind an endpoint. For information about name and address formats, please consult the documentation provided for the protocol you are using.

If the protocol supports it, you can specify a name pattern rather than a name when calling the OTLookupName function. Different protocols might use different wildcard characters to define name patterns. Please consult the documentation provided for your protocol to determine valid wildcard characters and how you use these to specify name patterns.

Searching for Names

You use the OTLookupName function to search for a registered name or for a list of names if your protocol supports name pattern matching. You use the req parameter to specify the name or name pattern to search for. When the function returns, it uses the reply parameter to pass back the matching name or names.

The req parameter is a pointer to a TLookupRequest structure containing the name or name pattern to be found and additional information that the mapper can use in conducting the search. You use the maxcnt field to specify the number of names you expect to be returned. If you are looking for a specific name, set this field to 1. If you are looking for a name pattern, you can use this field to indicate the number of matches you expect the OTLookupName function to return. You use the timeout field to specify the amount of time (in milliseconds) available for this search. If a match is not found within the specified time, the function returns with the kOTNoDataErr. If the number you specify for the maxcnt field is larger than the number of names that match the given pattern, the mapper provider uses the value given in the timeout field to determine when to stop the search.

The reply parameter is a pointer to a TLookupReply structure that contains two fields. The names field describes the size and location of the buffer in which the replies are placed when the function returns; the rspcount field specifies the number of matching entries found. Figure 6-1 shows how the contents of a reply buffer containing two entries are stored. The section "Code Sample: Using OTLookupName" provides and describes a sample program that uses the OTLookupName function. See especially, Listing 6-3.

Figure 6-1 Format of entries in OTLookupName reply buffer

The first two bytes of each entry specify the length of the address; the second two bytes specify the length of the name. The address is stored next and then the name, padded to a four byte boundary.

Retrieving Entries in Asynchronous Mode

If you call the OTLookupName function asynchronously, you can use an alternate method for retrieving matching entries. In asynchronous mode, this function sends two event codes: it sends the T_LKUPNAMERESULT code each time it stores a name in the reply buffer, and it sends the T_LKUPNAMECOMPLETE code when it has stored the last name in the reply buffer--that is, when the function as a whole completes execution. Each time the T_LKUPNAMERESULT event is passed to your notification function, you can do the following:

  1. Copy the name and address information from the reply buffer to some other location.

  2. From inside the notifier function, set the reply->names.len field or the reply->rspcount field to 0.

    When you set either of these fields to 0, Open Transport automatically sets the other field to 0. It's important, however, that you reset these values from within the notifier or the results might be unpredictable. You can also do it from code bracketed by the OTEnterNotifier and OTLeaveNotifier functions. For more information, see "OTEnterNotifier".

  3. Repeat the first two steps until the event passed to your notifier function is T_LKUPNAMECOMPLETE.

This method saves you the trouble of guessing how large a reply buffer to allocate. It might also save you some memory if you are expecting many matches to be returned and are interested in only some of them.

Note
The T_LKUPNAMECOMPLETE event might have stored a name in the buffer. Be sure to check for this possibility.

Code Sample: Using OTLookupName

This section discusses the program OTLookupNameTest, which demonstrates how you open an NBP mapper provider, issue an NBP lookup request, and print out the resulting information. Listing 6-1 shows the preprocessor directives and the main function of the program.

Listing 6-1 The main function to OTLookupNameTest

#ifndef qDebug    /* variable set for OT debugging macros */
#define qDebug1
#endif

#include <OpenTransport.h>
#include <OpenTptAppleTalk.h>
#include <OTDebug.h> /* Need OTDebugBreak & OTAssert macros */
#include <stdio.h>

/* OTDebugStr is not defined in OT header files, but it is
exported by the libraries, so we define the prototype here. */
extern pascal void OTDebugStr(const char* str);

static UInt32 gLastPrinted = 0; /* Global var to track printing */

void main(void)
{  OSStatus err;
   char     requestAddress[] = "=:AFPServer@*";
   
   printf("Hello World!\n");

   err = InitOpenTransport();
   if (err == noErr) {
      err = LookupAndPrint(requestAddress);
      CloseOpenTransport();
   }
   if (err == noErr) {
      printf("Success.\n");
   } else {
      printf("Failed with error %d.\n", err);
   }
   printf("Done.  Press command-Q to Quit.\n");
}
The main function initializes Open Transport, calls the user-defined function LookupAndPrint (passing a value for the requested address), and then closes Open Transport.

The LookupAndPrint function is the key function to the OTLookupNameTest program. However, because it calls Open Transport functions synchronously, it also uses a notifier to yield time to other processes. Listing 6-2 shows the notifier, which calls printf periodically in response to a kOTSyncIdle event. (The printf function calls WaitNextEvent, thus our synchronous calls to Open Transport will yield time to other processes. A real world application would probably use threads to do this.

Listing 6-2 Notifier that yields time to other processes

static pascal void YieldingNotifier(EndpointRef ep, OTEventCode code, 
                              OTResult result, void* cookie)
{
   #pragma unused(ep)
   #pragma unused(result)
   #pragma unused(cookie)
   
   switch (code) {
      case kOTSyncIdleEvent:
         if (TickCount() > gLastPrinted + 10) {
            printf(".");
            fflush(stdout);
            gLastPrinted = TickCount();
         }
         break;
      default:
         /* do nothing */
         break;
   }
}
For more information on using threads to yield time, see "Using Synchronous Processing With Threads".

Listing 6-3 shows the LookupAndPrint function. This function takes one parameter, a pointer to an NBP address. This address must have the form

<name>:<type>@<zone>

The function begins by opening an NBP mapper provider and switching it into synchronous/blocking mode. It uses kOTSyncIdle events (and the notifier shown in Listing 6-2) to yield time to other processes. Then it issues an NBP lookup request, using the OTLookUpName function . When the request completes, the function calls the user-defined PrintAddress and PrintName functions to display the results.

Listing 6-3 The LookupAndPrint function

static OSStatus LookupAndPrint(char *requestAddress)
{
   OSStatus    err;
   OSStatus    junk;
   MapperRef   nbpMapper;
   TLookupRequest lookupRequest;
   TLookupReply lookupReply;
   UInt8       *responseBuffer;
   TLookupBuffer*currentLookupReplyBuffer;
   UInt32       nameIndex;
   
   err = noErr;
   nbpMapper = kOTInvalidMapperRef; /* for error checking */
   
   /* Create the responseBuffer. */
   
   responseBuffer = OTAllocMem(kResponseBufferSize);
   if (responseBuffer == nil) 
      err = kENOMEMErr;
   
   /* Create an NBP mapper and set it to up for threaded processing. */
   
   if (err == noErr)
      nbpMapper = OTOpenMapper(OTCreateConfiguration(kNBPName),
                                       0, &err);
   if (err == noErr) {
      junk = OTSetSynchronous(nbpMapper);
      OTAssert("LookupAndPrint: Could not set synchronous mode
                                 on mapper", junk == noErr);
      junk = OTSetBlocking(nbpMapper);
      OTAssert("LookupAndPrint: Could not set blocking mode 
                              on mapper", junk == noErr);
      junk = OTUseSyncIdleEvents(nbpMapper, true);
      OTAssert("LookupAndPrint: Could not enable sync idle events
                              on mapper", junk == noErr);
      junk = OTInstallNotifier(nbpMapper, YieldingNotifier, nil);
      OTAssert("LookupAndPrint: Could not install notifier
                              for mapper", junk == noErr);
   }
   
   /* Call OTLookupName synchronously. */
   
   if (err == noErr) {
      
      /* Set up the TLookupRequest structure. */

      OTMemzero(&lookupRequest, sizeof(lookupRequest));
      lookupRequest.name.buf = (UInt8 *) requestAddress;
      lookupRequest.name.len = OTStrLength(requestAddress);
      lookupRequest.timeout = 1000;// 1 second in milliseconds
      lookupRequest.maxcnt = kResponseBufferSize /
                           kNBPEntityBufferSize;

      /* Set up the TLookupReply structure. */
      
      OTMemzero(&lookupReply, sizeof(lookupReply));
      lookupReply.names.buf = responseBuffer;
      lookupReply.names.maxlen = kResponseBufferSize;
      
      /* Now do the lookup. */
      
      err = OTLookupName(nbpMapper, &lookupRequest, &lookupReply);
   }
   
   /* Print out the contents of the responseBuffer. */
   
   if (err == noErr) {
      printf("\n");
      
      /* Start by pointing to the beginning of the response buffer. */
      
      currentLookupReplyBuffer = (TLookupBuffer *) responseBuffer;
      
      /* For each response in the buffer... */
      
      for (nameIndex = 0; nameIndex < lookupReply.rspcount;
                                 nameIndex++) {

      /*... print the name and address and... */
      
         printf("%3d ", nameIndex);
         PrintAddress( (DDPAddress *)
                  &currentLookupReplyBuffer->fAddressBuffer[0]);
         PrintName( (char *)&currentLookupReplyBuffer->
                  fAddressBuffer[currentLookupReplyBuffer->
                                    fAddressLength], 
                     currentLookupReplyBuffer->fNameLength);
         printf("\n");
         
         /*... use OTNextLookupBuffer to get from the current
                                 buffer to the next. */
         
         currentLookupReplyBuffer =
               OTNextLookupBuffer(currentLookupReplyBuffer);
      }
   }
   
   /* Clean up. */
   
   if (responseBuffer != nil) {
      OTFreeMem(responseBuffer);
   }
   if (nbpMapper != kOTInvalidMapperRef) {
      junk = OTCloseProvider(nbpMapper);
      OTAssert("LookupAndPrint: Failed closing mapper", junk == noErr);
   }
   
   return err;
}
The function LookupAndPrint calls two functions, PrintName and PrintAddress, to print names and addresses; Listing 6-4 shows the two functions.

Listing 6-4 Printing names and addresses

static void PrintName(const char *name, UInt32 length)
{
   char nameForPrinting[256];
   
   OTMemzero(nameForPrinting, 256);
   OTMemcpy(nameForPrinting, name, length);

   printf(""%s"", nameForPrinting);
}
static void PrintAddress( DDPAddress *addr )
{
	OTAssert( "PrintAddress: Expected a DDPNBPADdress",
						addr->fAddressType == AF_ATALK_DDP );
	printf("Net = $%04x, Node = $%02x, Socket = $%02x ", 
				addr->fNetwork, 
				addr->fNodeID, 
				addr->fSocket);
}



Subtopics
Setting Modes of Operation for Mappers
Specifying Name and Address Information
Searching for Names
Retrieving Entries in Asynchronous Mode
Code Sample: Using OTLookupName

Previous Book Contents Book Index Next

© Apple Computer, Inc.
15 JAN 1998