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 /
Chapter 6 - AppleTalk Transaction Protocol (ATP) / Using ATP


Writing a Responder ATP Application

A responder application receives incoming ATP requests, processes them, and sends a response to the requester application. To write a responder application, you open a socket that you set up to listen for requests. When you receive a request, you process it and send a response back to the requester application. The response can consist of a message reporting the outcome of the processing you performed or data resulting from the processing.

Before you can use ATP, you must first open the .MPP driver, which in turn opens the .ATP driver. Use the Device Manager's OpenDriver function to open the .MPP driver. Even if you suspect that the .MPP and the .ATP drivers are open, you should call the OpenDriver function for the .MPP driver to ensure that this is the case. Calling OpenDriver for a driver that is already open will not produce harmful repercussions. See the chapter "Device Manager" in Inside Macintosh: Devices for information on the OpenDriver function. Do not close the .MPP driver when you are finished using ATP because other applications dependent on it or the .ATP driver require that it remain open.

Opening and Setting Up a Socket to Receive Requests

To open a socket to receive incoming requests, you use the following procedure:

  1. To open the socket, call the POpenATPSkt function, providing it with values as follows:

    • To direct ATP to open a specific socket, provide the number of that socket as the value of the atpSocket parameter; to allow ATP to dynamically assign a socket, specify 0 as the value of this field.
    • To filter the sockets from which you will accept requests, set the internet socket address fields of the addrBlock parameter; to accept requests from any socket,
      set all three fields to 0. You can filter requests based on network, socket, or node numbers. For example, to accept requests from all sockets on the node whose ID
      is 112, you set the network and socket number fields of the address block record to 0 and the node ID field to 112.

  2. To set up the socket to receive requests, call the PGetRequest function, which listens for an incoming request on the socket you specify. You provide it with the parameter values as follows:

    • Allocate a buffer to store the incoming request; you pass PGetRequest a pointer to this buffer and the length of the buffer. Unless you know the exact size of the incoming request, allocate at least 578 bytes of nonrelocatable memory for this buffer to accommodate the maximum request packet size. Set the reqPointer parameter to point to the buffer, and set the reqLength parameter to the size in bytes of the buffer.
    • Set the atpSocket parameter to the number of the socket to be used to listen for the request; this is the socket you opened through the POpenATPSkt call.
    • Set the ioCompletion parameter. In most cases, you should issue the PGetRequest call asynchronously so that your application can continue execution while PGetRequest listens for an incoming call; the PGetRequest function returns after it receives an incoming request or encounters an error condition. If you issue this call asynchronously, you must either specify a completion routine or set the ioCompletion parameter to NIL. If you use a completion routine, before it exits, your completion routine can call the PGetRequest function again to listen for the next incoming request. If you do not use a completion routine, you must poll the ioResult field for indication of an incoming request to determine when the function completes execution and whether an error condition or an incoming request caused the function to complete. For more information on calling a routine asynchronously, see the chapter "Introduction to AppleTalk" in this book.

  3. Process the values that PGetRequest returns. The PGetRequest function returns the following values that may be of use to your application:

    • The request transaction ID reqTID that ATP assigns to this request. If you intend to respond to the request, save this value because you will need to pass it to the PSendResponse function and the PAddResponse function to identify the request for which the response message is intended. For more information on the trans-
      action ID, see the discussion in the section "The ATP Packet Format" beginning on page 6-5.
    • The userData parameter, which contains any additional information that the requester application has sent. To make this parameter meaningful, both the requester and the responder applications should agree on the use of these additional data bytes that are separate from the request or response data sent
      in an ATP transaction.
    • The exactly-once bit (bit 5) of the atpFlags parameter, which is set if the request received is part of an exactly-once transaction. ATP uses this information internally to ensure that your responder application receives this request only once.

Listing 6-2 on page 6-17 shows how to open a socket and issue a call to the PGetRequest function to receive requests.

Responding to Requests

After you process a request and create a response message, you call the PSendResponse function to send the response. ATP assembles the response packets into a message and returns them to the requester application. You can send the request through the same socket that you use to receive incoming requests, or you can specify a different socket to be used for this purpose. To use a different socket, you must first open the socket by calling POpenATPSocket. The code in Listing 6-2 opens a new socket that it uses to
send the response.

  1. Create a buffer data structure to hold the response data that you want to send.

    The buffer data structure (BDS) must be an array of up to eight elements. You can use the BuildBDS function to create the BDS. You pass BuildBDS a pointer to a buffer and the length of the buffer, and it creates up to eight elements depending on the size of the buffer that you supply. BuildBDS returns as its function result the number of elements that it creates; you pass this number and a pointer to the buffer data structure to the PSendResponse call. The memory that you allocate for the buffer must be nonrelocatable until the PSendResponse call completes execution. After PSendResponse returns, you should release this memory.

  2. To send the response, call the PSendResponse function. The response data cannot exceed 4624 bytes. If you need to send more information, you can follow the PSendResponse function with one or more calls to the PAddResponse function until you have sent a total of eight packets, including the packets that you sent
    when you called the PSendResponse function; each time you call the PAddResponse function, you can send one additional packet consisting of 578 bytes of data.

    • For the input address block (addrBlock) and transaction ID (transID) parameters to PSendResponse, use the address block (addrBlock) and
      request transaction ID (reqTID) parameter values that the PGetRequest
      function returned.
    • Set the numOfBuffs field to the number of response packets that you are sending. If you are sending fewer packets than the requester expects to receive, you must set the end-of-message (atpEOMvalue) bit (bit 4) in the atpFlags field to indicate that the last packet is the final one in the response message. The bitmap returned by the PGetRequest function indicates the number of packets that the requester expects in response.
    • Set the atpSocket field to the number of the socket that you are using to send
      the response.

  3. Call the CloseATPSkt function to close the socket that you opened to receive requests and respond to them after you are finished with this socket. You can use
    the socket to continue to listen for requests until your application completes execution, but you should explicitly close the socket before exiting the program.

The code in Listing 6-2 first shows how to open a socket and issue a call to the PGetRequest function to receive requests. Then it shows how to prepare the
response data and send it.

Listing 6-2 Opening a socket to receive a request and sending response data

CONST
   kMaxPacketSize = 578;            {maximum packet size you can receive}
   kMaxResponses = 8;               {maximum number of responses to expect}
   kRespBufSize = kMaxPacketSize * kMaxResponses;
                                    {your response buffer}
VAR
   err:              OSErr;
   NumOfBufs:        Integer;
   ref:              Integer;
   nBufs:            Integer;
   ReqBitMap:        BitMapType;
   thisBit:          LongInt;
   gAtpPBPtr:        ATPPBPtr;
   gSendRespPBPtr:   ATPPBPtr;
   gGetReqBufPtr:    Ptr;
   gSRespBuf:        Ptr;
   gSRespBdsPtr:     BDSPtr;
BEGIN
   gAtpPBPtr := ATPPBPtr(NewPtr(SizeOf(ATPParamBlock)));
   gSendRespPBPtr := ATPPBPtr(NewPtr(SizeOf(ATPParamBlock)));
   gGetReqBufPtr := NewPtr(kMaxPacketSize);
   gSRespBdsPtr := BDSPtr(NewPtr(SizeOf(BDSType)));
   gSRespBuf := NewPtr(kRespBufSize);

   err := OpenDriver('MPP',ref);
   if err <> noErr THEN DoErr(err);

WITH gAtpPBPtr^ DO
BEGIN
   atpSocket := 0;                  {dynamically allocate a socket}
   addrBlock.aNet := 0;             {accept requests from anyone}
   addrBlock.aNode := 0;
   addrBlock.aSocket := 0;
END;
err := POpenATPSkt(gAtpPBPtr,false);{socket is returned in }
                                    { gAtpPBPtr^.atpSocket}

IF err <> noErr THEN DoErr(err);
IF gAtpPBPtr^.ioResult <> noErr THEN DoErr(err);
WITH gAtpPBPtr^ DO
BEGIN
   reqLength := 0;                  {request data length will be returned }
                                    { to you here}
   reqPointer := gGetReqBufPtr;     {pointer to buffer for incoming request }
                                    { data}
END;
   err := PGetRequest(gAtpPBPtr,TRUE);{asynchronous PGetRequest}

   IF err <> noErr THEN DoErr(err);

   {Poll ioResult until the call completes.}
   WHILE gAtpPBPtr^.ioResult > noErr DO
   BEGIN
      GoDoSomething;                {return control to user while you wait }
                                    { for PGetRequest to complete}
   END;
   IF gAtpPBPtr^.ioResult <> noErr THEN DoErr(err);

   MyProcessRequestReceived(gAtpPBPtr^.reqPointer,gAtpPBPtr^.reqLength)
                                    {user routine that looks at the request }
                                    { data received}

   {Walk through the bitmap and see how many response buffers you need.}
   NumOfBufs := 0;
   FOR thisBit := 0 to 7 DO
   BEGIN
      {Each bit that is set corresponds to a buffer.}
      if BitTst(@gAtpPBPtr^.bitMap,thisBit) = TRUE THEN
   BEGIN
      {Your routine to fill in the appropriate response data.}
      SetUpResponseData(gSRespBuf,thisBit);
      NumOfBufs := NumOfBufs + 1;
   END
END;

{Put your response data into the BDS structure.}
nBufs := BuildBDS(gSRespBuf,Ptr(gSRespBdsPtr),(NumOfBufs * kMaxPacketSize));





WITH gSendRespPBPtr^ DO
BEGIN
   atpSocket := gAtpPBPtr^.atpSocket;
   atpFlags := atpEOMvalue;         {indicate end of message}

{Send response to the machine that sent you the request.}
   addrBlock.aNet := gAtpPBPtr^.addrBlock.aNet;
   addrBlock.aNode := gAtpPBPtr^.addrBlock.aNode;
   addrBlock.aSocket := gAtpPBPtr^.addrBlock.aSocket;
   bdsPointer := Ptr(gSRespBdsPtr);
   numOfBuffs := NumOfBufs;         {send all of the responses back now}
   bdsSize := nBufs;                {indicate how many responses you are }
                                    { sending}
   transID := gAtpPBPtr^.transID;   {use transID returned from the }
                                    { PGetRequest function}
END;
err := PSendResponse(gSendRespPBPtr,FALSE);

IF err <> noErr THEN DoErr(err);

{Clean up after you are done.}
DisposePtr(Ptr(gAtpPBPtr));
DisposePtr(Ptr(gSendRespPBPtr));
DisposePtr(gGetReqBufPtr);
DisposePtr(Ptr(gSRespBdsPtr));
DisposePtr(gSRespBuf);
END.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996