Important: The information in this document is obsolete and should not be used for new development.
Opening and Maintaining an ADSP Connection
To use ADSP to establish and maintain a connection between a socket on your local node and a remote socket, use the following procedure:
Listing 5-1 illustrates the use of ADSP. This routine opens the .MPP and .DSP drivers and allocates memory for its internal data buffers, for the CCB, and for the send, receive, and attention-message buffers. Then the routine uses the
- Use the Device Manager's
OpenDriverfunction to open the .MPP driver, and then use it again to open the .DSP driver. The .MPP driver must be open before you open the .DSP driver. TheOpenDriverfunction call for the .DSP driver returns the driver reference number. You must supply this reference number each time you call the Device Manager'sPBControlfunction to execute an ADSP routine.- Allocate nonrelocatable memory for a CCB, send and receive queues, and an attention-
message buffer. If you need to allocate the memory dynamically while the program
is running, use theNewPtrroutine. Otherwise, the way in which you allocate the memory depends on the compiler you are using. (Listing 5-1 on page 5-17 shows how to do this in Pascal.) The memory that you allocate becomes the property of ADSP when you call thedspInitroutine to establish a connection end. You cannot write any data to this memory except by calling ADSP, and you must ensure that the memory remains locked until you call thedspRemoveroutine to eliminate the connection end.The CCB is 242 bytes. The attention-message buffer must be 570 bytes. When you send bytes to a remote connection end, ADSP stores the bytes in a buffer called the send queue. Until the remote connection end acknowledges their receipt, ADSP keeps the bytes you sent in the send queue so that they are available to be retransmitted if necessary. When the local connection end receives bytes, it stores them in a buffer, called the receive queue, until you read them. The sizes you need for the send and receive queues depend on the lengths of the messages being sent.
ADSP does not transmit data from the remote connection end until there is room for
it in your receive queue. If your send or receive queues are too small, they limit the speed with which you can transmit and receive data. A queue size of 600 bytes should work well for most applications. If you are using ADSP to send a continuous flow
of data, a larger data buffer improves performance. If your application is sending or receiving the user's keystrokes, a smaller buffer should be adequate. The constantminDSPQueueSize, which is defined in the MPW interface file for ADSP, indicates the minimum queue size that you can use.If you are using a version of the .DSP driver prior to version 1.5, you must allocate send and receive queues that are 12 percent larger than the actual buffer sizes you need. You must do this in order to provide some extra space for use by the .DSP driver. Version 1.5 and later versions of the .DSP driver use a much smaller, and variable, portion of buffer space for overhead. The .DSP driver version number is stored in the low byte of the
qFlagsfield, which is the first field in thedCtlQHdrfield in the driver's device control entry (DCE) data structure. Version 1.5 of the
.DSP driver has a version number of 4 in the DCE. See the chapter "Device Manager" in Inside Macintosh: Devices for information on the DCE.- Use the
dspInitroutine to establish a connection end. You must provide pointers
to the CCB, send queue, receive queue, and attention-message buffer. You may also provide a pointer to a user routine that ADSP calls when your connection end
receives an unsolicited connection event. See the section"Writing a User Routine for Connection Events" on page 5-26 for information on providing a user routine.If there is a specific socket that you want to use for the connection end, you can specify the socket number in the
localSocketparameter. If you want ADSP to assign the socket for you, specify 0 for thelocalSocketparameter; in this case, ADSP returns the socket number when thedspInitroutine completes execution.- If you wish, you can use the Name-Binding Protocol (NBP) routines to add the name and address of your connection end to the node's names table. See the chapter "Name-Binding Protocol (NBP)" in this book for information on NBP.
- You can use the
dspOptionsroutine to set several parameters that control the behavior of the connection end. Because every parameter has a default value, the use of thedspOptionsroutine is optional. You can specify values for the following parameters:
- The
sendBlockingparameter, which sets the maximum number of bytes that may accumulate in the send queue before ADSP sends a packet to the remote connection end. You can experiment with different values of thesendBlockingparameter to determine which provides the best performance. Under most circumstances, the default value of 16 bytes gives good performance.- The
badSeqMaxparameter, which sets the maximum number of out-of-sequence data packets that the local connection end can receive before requesting the remote connection end to retransmit the missing data. Under most circumstances, the default value of 3 provides good performance.- The
useCheckSumparameter, which determines whether the Datagram Delivery Protocol (DDP) should compute a checksum and include it in each packet that it sends to the remote connection end. Using checksums slows communications slightly. Normally ADSP and DDP perform enough error checking to ensure safe delivery of all data. Set theuseCheckSumparameter to 1 only if you feel that the network is highly unreliable.
- Call the
dspOpenroutine to open the connection. ThedspOpenroutine has four possible modes of operation:ocAccept,ocEstablish,ocRequest, andocPassive. Normally you use either theocRequestorocPassivemode. You must specify one of these four modes for theocModeparameter when you call
thedspOpenroutine.The
ocAcceptmode is used only by connection servers. TheocEstablishmode
is used by routines that determine their connection-opening parameters and establish a connection independently of ADSP, but use ADSP to transmit and receive data.Use the
ocRequestmode when you want to establish communications with a specific socket on the AppleTalk internet. When you execute thedspOpenroutine
in theocRequestmode, ADSP sends an open-connection request to the address
you specify.If the socket to which you send the open-connection request is a connection listener, the connection server that operates that connection listener can select any socket
on the internet to be the connection end that responds to the open-connection request. To restrict the socket from which you will accept a response to your open-connection request, specify a value for thefilterAddressparameter to thedspOpenroutine. When your connection end receives a response from a socket that meets the restrictions of thefilterAddressparameter, it acknowledges the response and ADSP completes the connection.To use the
ocRequestmode, you must know the complete internet address of the remote socket, and the ADSP client at that address must either be a connection listener or have executed thedspOpenroutine in theocPassivemode. You can use the NBP routines to obtain a list of names of objects on the internet and to determine the internet address of a socket when you know its name. See the chapter "Name-Binding Protocol (NBP)" in this book for information on the NBP routines.Use the
ocPassivemode when you expect to receive an open-connection request from a remote socket. You can specify a value for thefilterAddressparameter to restrict the network number, node ID, or socket number from which you will accept an open-connection request. When your connection end receives an open-connection request that meets the restrictions of thefilterAddressparameter, it acknowledges the request and ADSP completes the connection.You can poll the state field in the CCB to determine when the connection end is waiting to receive an open-connection request, when the connection end is waiting to receive an acknowledgment of an open-connection request, and when the connection is open. See the section "The ADSP Connection Control Block Record" beginning on page 5-35 for a description of the CCB fields. Alternatively, you can check the result code for the
dspOpenroutine when the routine completes execution. If the routine returns thenoErrresult code, then the connection is open.- Use the
dspReadroutine to read data that your connection end has received from
the remote connection end. Use thedspWriteroutine to send data to the remote connection end. Use thedspAttentionroutine to send attention messages to the remote connection end.The
dspWriteroutine places data in the send queue. ADSP is a full-duplex, symmetric communications protocol: You can send data at any time, and your connection end can receive data at any time, even at the same time as you are sending data. ADSP transmits the data in the send queue when one of the following conditions occurs:
- You call the
dspWriteroutine with the flush parameter set to a nonzero number.- The number of bytes in the send queue equals or exceeds the blocking factor that you set with the
dspOptionsroutine.- The send timer expires. The send timer sets the maximum amount of time that can pass before ADSP sends all unsent data in the send queue to the remote connection end. ADSP calculates the best value to use for this timer and sets it automatically.
- A connection event requires that the local connection end send an acknowledgment packet to the remote connection end.
If you send more data to the send queue than it can hold, the
dspWriteroutine does not complete execution until it has written all the data to the send queue. If you execute thedspWriteroutine asynchronously, ADSP returns control to your program and writes the data to the send queue as quickly as it can. This technique provides the most efficient use of the send queue by your program and by ADSP. Because ADSP does not remove data from the send queue until that data has been not only sent but also acknowledged by the remote connection end, using theflushparameter to thedspWriteroutine does not guarantee that the send queue is empty. You can use
thedspStatusroutine to determine how much free buffer space is available in the send queue.The
dspReadroutine reads data from the receive queue into your application's private data buffer. ADSP does not transmit data until there is space available in
the other end's receive queue to accept it. Because a full receive queue slows the communications rate, you should read data from the receive queue as often as necessary to keep sufficient buffer space available for new data. You can use either
of two techniques to do this:- Allocate a small receive queue (about 600 bytes) and call the
dspReadroutine asynchronously. Your completion routine for thedspReadroutine should then
call thedspReadroutine again.- Allocate a large receive queue and call the
dspReadroutine less frequently.If there is less data in the receive queue than the amount you specify with the
reqCountparameter to thedspReadcommand, the command does not complete execution until there is enough data available to satisfy the request. There are three exceptions to this rule:- If the end-of-message bit in the ADSP packet header is set, the
dspReadcommand reads the data in the receive queue, returns the actual amount of data read in theactCountparameter, and returns theeomparameter set to 1.- If you have closed the connection end before calling the
dspReadroutine (that is, the connection is half open), the command reads whatever data is available and returns the actual amount of data read in theactCountparameter.- If ADSP has closed the connection before you call the
dspReadroutine and there is no data in the receive queue, the routine returns thenoErrresult code with theactCountparameter set to 0 and theeomparameter set to 0.In addition to the byte-stream data format implemented by the
dspReadanddspWriteroutines, ADSP provides a mechanism for sending and receiving control signals or information separate from the byte stream. You use thedspAttentionroutine to send an attention code and an attention message to the remote connection end. When your connection end receives an attention message, ADSP's interrupt handler sets theeAttentionflag in theuserFlagsfield of the CCB and calls your user routine. Your user routine must first clear theuserFlagsfield. Then your routine can read the attention code and attention message and take whatever action you deem appropriate.Because ADSP is often used by terminal emulation programs and other applications that pass the data they receive on to the user without processing it, attention messages provide a mechanism for the applications that are clients of the connection ends to communicate with each other. For example, you could use attention messages to implement a handshaking and data-checking protocol for a program that transfers disk files between two applications, neither one of which is a file server. Or a database server on a mainframe computer that uses ADSP to communicate with Macintosh computer workstations could use the attention mechanism to inform the workstations when the database is about to be closed down for maintenance.
- When you are ready to close the ADSP connection, you can use the
dspCloseordspRemoveroutine to close the connection end. Use thedspCloseroutine if you intend to use that connection end to open another connection and do not want
to release the memory you allocated for the connection end. Use thedspRemoveroutine if you are completely finished with the connection end and want to release
the memory.You can continue to read data from the receive queue after you have called the
dspCloseroutine, but not after you have called thedspRemoveroutine. You can
use thedspStatusroutine to determine whether any data is remaining in the receive queue, or you can read data from the receive queue until both theactCountandeomfields of thedspReadparameter block return 0.If you set the
abortparameter for thedspCloseordspRemoveroutine to 0, then ADSP does not close the connection or the connection end until it has sent--and received acknowledgment for--all data in the send queue and any pending attention messages. If you set theabortparameter to 1, then ADSP discards any data in the send queue and any attention messages that have not already been sent.After you have executed the
dspRemoveroutine, you can release the memory you allocated for the CCB and data buffers.
dspInitroutine to establish a connection end and uses NBP to register the name of the connection end on the internet. (The user routine specified by theuserRoutineparameter to thedspInitfunction is shown in Listing 5-3 on page 5-28.) Next, Listing 5-1 uses thedspOptionsroutine to
set the blocking factor to 24 bytes. This routine then uses NBP to determine the address of a socket whose name was selected by the user and sends an open-connection request (dspOpen) to that socket. When thedspOpenroutine completes execution, it sends data and an attention message to the remote connection end and reads data from its receive queue. Finally, the routine closes the connection end with thedspRemoveroutine and releases the memory it allocated.Listing 5-1 Using ADSP to establish and use a connection
PROCEDURE MyADSP; CONST qSize = 600; {queue space} myDataSize = 128; {size of internal read/write buffers} blockFact = 24; {blocking factor} TYPE {Modify the connection control block to add storage for A5.} myTRCCB = RECORD myA5: LongInt; u: TRCCB; END; VAR dspSendQPtr: Ptr; dspRecvQPtr: Ptr; dspAttnBufPtr: Ptr; myData2ReadPtr: Ptr; myData2WritePtr: Ptr; myAttnMsgPtr: Ptr; dspCCB: myTRCCB; myDSPPBPtr: DSPPBPtr; myMPPPBPtr: MPPPBPtr; myNTEName: NamesTableEntry; myAddrBlk: AddrBlock; drvrRefNum: Integer; mppRefNum: Integer; connRefNum: Integer; gReceivedAnEvent: Boolean; myAttnCode: Integer; tempFlag: Byte; tempCFlag: Integer; myErr: OSErr; BEGIN myErr := OpenDriver('.MPP', mppRefNum); {open .MPP driver} IF myErr <> noErr THEN DoErr(myErr); {check and handle error} myErr := OpenDriver('.DSP', drvrRefNum); {open .DSP driver} IF myErr <> noErr THEN DoErr(myErr); {check and handle error} {Allocate memory for data buffers.} dspSendQPtr := NewPtr(qSize); {ADSP use only} dspRecvQPtr := NewPtr(qSize); {ADSP use only} dspAttnBufPtr := NewPtr(attnBufSize); {ADSP use only} myData2ReadPtr := NewPtr(myDataSize); myData2WritePtr := NewPtr(myDataSize); myAttnMsgPtr := NewPtr(myDataSize); myDSPPBPtr := DSPPBPtr(NewPtr(SizeOf(DSPParamBlock))); myMPPPBPtr := MPPPBPtr(NewPtr(SizeOf(MPPParamBlock))); WITH myDSPPBPtr^ DO {set up dspInit parameters} BEGIN ioCRefNum := drvrRefNum; {ADSP driver ref num} csCode := dspInit; ccbPtr := @dspCCB; {pointer to CCB} userRoutine := @myConnectionEvtUserRoutine; {see Listing 5-3} sendQSize := qSize; {size of send queue} sendQueue := dspSendQPtr; {send-queue buffer} recvQSize := qSize; {size of receive queue} recvQueue := dspRecvQPtr; {receive-queue buffer} attnPtr := dspAttnBufPtr; {receive-attention buffer} localSocket := 0; {let ADSP assign socket} END; gReceivedAnEvent := FALSE; dspCCB.myA5 := SetCurrentA5; {save A5 for the user routine} {Establish a connection end.} myErr := PBControl(ParmBlkPtr(myDSPPBPtr), FALSE); IF myErr <> noErr THEN DoErr(myErr); {check and handle error} connRefNum := myDSPPBPtr^.ccbRefNum; {save CCB ref num for later} NBPSetNTE(@myNTEName, 'The Object', 'The Type', '*', myDSPPBPtr^.localSocket); {set up NBP names table entry} WITH myMPPPBPtr^ DO {set up PRegisterName } { parameters} BEGIN interval := 7; {retransmit every 7*8=56 ticks} count := 3; {retry 3 times} entityPtr := @myNTEName; {name to register} verifyFlag := 1; {verify this name} END; {Register this socket.} myErr := PRegisterName(myMPPPBPtr, FALSE); {register this socket} IF myErr <> noErr THEN DoErr(myErr); {check and handle error} WITH myDSPPBPtr^ DO {set up dspOptions parameters} BEGIN ioCRefNum := drvrRefNum; {ADSP driver ref num} csCode := dspOptions; ccbRefNum := connRefNum; {connection ref num} sendBlocking := blockFact; {quantum for data packet} badSeqMax := 0; {use default} useCheckSum := 0; {don't calculate checksum} END; myErr := PBControl(ParmBlkPtr(myDSPPBPtr), FALSE); {set options} IF myErr <> noErr THEN DoErr(myErr); {check and handle error} PickASocket(myAddrBlk); {routine using the PLookupName } { function to pick a socket } { for the connection} {Open a connection with the selected socket.} WITH myDSPPBPtr^ DO {set up dspOpen parameters} BEGIN ioCRefNum := drvrRefNum; {ADSP driver ref num} csCode := dspOpen; ccbRefNum := connRefNum; {connection ref num} remoteAddress := myAddrBlk; {address of remote socket } { from PLookupName function} filterAddress := myAddrBlk; {address filter,specified } { socket address only} ocMode := ocRequest; {open connection mode} ocInterval := 0; {use default retry interval} ocMaximum := 0; {use default retry maximum} END; myErr := PBControl(ParmBlkPtr(myDSPPBPtr), FALSE); {open a connection} IF myErr <> noErr THEN DoErr(myErr); {check and handle error} {The connection with the selected socket is open, so now send } { to the send queue exactly myDataSize number of bytes.} WITH myDSPPBPtr^ DO {set up dspWrite parameters} BEGIN ioCRefNum := drvrRefNum; {ADSP driver ref num} csCode := dspWrite; ccbRefNum := connRefNum; {connection ref num} reqCount := myDataSize; {write this number of bytes} dataPtr := myData2WritePtr; {pointer to send queue} eom := 1; {1 means last byte is } { logical end-of-message} flush := 1; {1 means send data now} END; myErr := PBControl(ParmBlkPtr(myDSPPBPtr), FALSE); {send data to the remote } { connection} IF myErr <> noErr THEN DoErr(myErr); {check and handle error} {Now send an attention message to the remote connection end.} WITH myDSPPBPtr^ DO {set up dspAttention parameters} BEGIN ioCRefNum := drvrRefNum; {ADSP driver ref num} csCode := dspAttention; ccbRefNum := connRefNum; {connection ref num} attnCode := 0; {user-defined attention code} attnSize := myDataSize; {length of attention message} attnData := myAttnMsgPtr; {attention message} END; myErr := PBControl(ParmBlkPtr(myDSPPBPtr), FALSE); IF myErr <> noErr THEN DoErr(myErr); {check and handle error} {Now read from the receive queue exactly myDataSize number } { of bytes.} WITH myDSPPBPtr^ DO {set up dspRead parameters} BEGIN ioCRefNum := drvrRefNum; {ADSP driver ref num} csCode := dspRead; ccbRefNum := connRefNum; {connection ref num} reqCount := myDataSize; {read this number of bytes} dataPtr := myData2ReadPtr; {pointer to read buffer} END; myErr := PBControl(ParmBlkPtr(myDSPPBPtr), FALSE); {read data from the remote } { connection} IF myErr <> noErr THEN DoErr(myErr); {check and handle error} {We're finished with the connection, so remove it.} WITH myDSPPBPtr^ DO {set up dspRemove parameters} BEGIN ioCRefNum := drvrRefNum; {ADSP driver ref num} csCode := dspRemove; ccbRefNum := connRefNum; {connection ref num} abort := 0; {don't close until } { everything is sent and } { received} END; myErr := PBControl(ParmBlkPtr(myDSPPBPtr), FALSE); {close and remove the } { connection} IF myErr <> noErr THEN DOErr(myErr); {check and handle error} {You're finished with this connection, so release the memory.} DisposPtr(dspSendQPtr); DisposPtr(dspRecvQPtr); DisposPtr(dspAttnBufPtr); DisposPtr(myData2ReadPtr); DisposPtr(myData2WritePtr); DisposPtr(myAttnMsgPtr); DisposPtr(Ptr(myDSPPBPtr)); DisposPtr(Ptr(myMPPPBPtr)); END; {MyADSP}