Important: The information in this document is obsolete and should not be used for new development.
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 theOpenDriver
function for the .MPP driver to ensure that this is the case. CallingOpenDrive
r for a driver that is already open will not produce harmful repercussions. See the chapter "Device Manager" in Inside Macintosh: Devices for information on theOpenDriver
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:
Listing 6-2 on page 6-17 shows how to open a socket and issue a call to the
- 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.
- 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 thereqPointer
parameter to point to the buffer, and set thereqLength
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 thePOpenATPSkt
call.- Set the
ioCompletion
parameter. In most cases, you should issue thePGetRequest
call asynchronously so that your application can continue execution whilePGetRequest
listens for an incoming call; thePGetRequest
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 theioCompletion
parameter toNIL
. If you use a completion routine, before it exits, your completion routine can call thePGetRequest
function again to listen for the next incoming request. If you do not use a completion routine, you must poll theioResult
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.
- Process the values that
PGetRequest
returns. ThePGetRequest
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 thePSendResponse
function and thePAddResponse
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.
PGetRequest
function to receive requests.Responding to Requests
After you process a request and create a response message, you call thePSendResponse
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 callingPOpenATPSocket
. The code in Listing 6-2 opens a new socket that it uses to
send the response.
The code in Listing 6-2 first shows how to open a socket and issue a call to the
- 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 passBuildBDS
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 thePSendResponse
call. The memory that you allocate for the buffer must be nonrelocatable until thePSendResponse
call completes execution. AfterPSendResponse
returns, you should release this memory.- 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 thePSendResponse
function with one or more calls to thePAddResponse
function until you have sent a total of eight packets, including the packets that you sent
when you called thePSendResponse
function; each time you call thePAddResponse
function, you can send one additional packet consisting of 578 bytes of data.
- For the input address block (
addrBlock
) and transaction ID (transID
) parameters toPSendResponse
, use the address block (addrBlock
) and
request transaction ID (reqTID
) parameter values that thePGetRequest
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 theatpFlags
field to indicate that the last packet is the final one in the response message. The bitmap returned by thePGetRequest
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.
- 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.
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.