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
OpenDriver
function 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. TheOpenDriver
function call for the .DSP driver returns the driver reference number. You must supply this reference number each time you call the Device Manager'sPBControl
function 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 theNewPtr
routine. 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 thedspInit
routine 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 thedspRemove
routine 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
qFlags
field, which is the first field in thedCtlQHdr
field 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
dspInit
routine 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
localSocket
parameter. If you want ADSP to assign the socket for you, specify 0 for thelocalSocket
parameter; in this case, ADSP returns the socket number when thedspInit
routine 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
dspOptions
routine to set several parameters that control the behavior of the connection end. Because every parameter has a default value, the use of thedspOptions
routine is optional. You can specify values for the following parameters:
- The
sendBlocking
parameter, 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 thesendBlocking
parameter to determine which provides the best performance. Under most circumstances, the default value of 16 bytes gives good performance.- The
badSeqMax
parameter, 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
useCheckSum
parameter, 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 theuseCheckSum
parameter to 1 only if you feel that the network is highly unreliable.
- Call the
dspOpen
routine to open the connection. ThedspOpen
routine has four possible modes of operation:ocAccept
,ocEstablish
,ocRequest
, andocPassive
. Normally you use either theocRequest
orocPassive
mode. You must specify one of these four modes for theocMode
parameter when you call
thedspOpen
routine.The
ocAccept
mode is used only by connection servers. TheocEstablish
mode
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
ocRequest
mode when you want to establish communications with a specific socket on the AppleTalk internet. When you execute thedspOpen
routine
in theocRequest
mode, 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 thefilterAddress
parameter to thedspOpen
routine. When your connection end receives a response from a socket that meets the restrictions of thefilterAddress
parameter, it acknowledges the response and ADSP completes the connection.To use the
ocRequest
mode, 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 thedspOpen
routine in theocPassive
mode. 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
ocPassive
mode when you expect to receive an open-connection request from a remote socket. You can specify a value for thefilterAddress
parameter 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 thefilterAddress
parameter, 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
dspOpen
routine when the routine completes execution. If the routine returns thenoErr
result code, then the connection is open.- Use the
dspRead
routine to read data that your connection end has received from
the remote connection end. Use thedspWrite
routine to send data to the remote connection end. Use thedspAttention
routine to send attention messages to the remote connection end.The
dspWrite
routine 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
dspWrite
routine 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
dspOptions
routine.- 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
dspWrite
routine does not complete execution until it has written all the data to the send queue. If you execute thedspWrite
routine 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 theflush
parameter to thedspWrite
routine does not guarantee that the send queue is empty. You can use
thedspStatus
routine to determine how much free buffer space is available in the send queue.The
dspRead
routine 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
dspRead
routine asynchronously. Your completion routine for thedspRead
routine should then
call thedspRead
routine again.- Allocate a large receive queue and call the
dspRead
routine less frequently.If there is less data in the receive queue than the amount you specify with the
reqCount
parameter to thedspRead
command, 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
dspRead
command reads the data in the receive queue, returns the actual amount of data read in theactCount
parameter, and returns theeom
parameter set to 1.- If you have closed the connection end before calling the
dspRead
routine (that is, the connection is half open), the command reads whatever data is available and returns the actual amount of data read in theactCount
parameter.- If ADSP has closed the connection before you call the
dspRead
routine and there is no data in the receive queue, the routine returns thenoErr
result code with theactCount
parameter set to 0 and theeom
parameter set to 0.In addition to the byte-stream data format implemented by the
dspRead
anddspWrite
routines, ADSP provides a mechanism for sending and receiving control signals or information separate from the byte stream. You use thedspAttention
routine 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 theeAttention
flag in theuserFlags
field of the CCB and calls your user routine. Your user routine must first clear theuserFlags
field. 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
dspClose
ordspRemove
routine to close the connection end. Use thedspClose
routine 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 thedspRemove
routine 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
dspClose
routine, but not after you have called thedspRemove
routine. You can
use thedspStatus
routine to determine whether any data is remaining in the receive queue, or you can read data from the receive queue until both theactCount
andeom
fields of thedspRead
parameter block return 0.If you set the
abort
parameter for thedspClose
ordspRemove
routine 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 theabort
parameter 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
dspRemove
routine, you can release the memory you allocated for the CCB and data buffers.
dspInit
routine to establish a connection end and uses NBP to register the name of the connection end on the internet. (The user routine specified by theuserRoutine
parameter to thedspInit
function is shown in Listing 5-3 on page 5-28.) Next, Listing 5-1 uses thedspOptions
routine 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 thedspOpen
routine 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 thedspRemove
routine 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}