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 5 - AppleTalk Data Stream Protocol (ADSP) / Using ADSP


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:

  1. 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. The OpenDriver function call for the .DSP driver returns the driver reference number. You must supply this reference number each time you call the Device Manager's PBControl function to execute an ADSP routine.
  2. 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 the NewPtr 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 the dspInit 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 the dspRemove 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 constant minDSPQueueSize, 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 the dCtlQHdr 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.

  3. 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 the localSocket parameter; in this case, ADSP returns the socket number when the dspInit routine completes execution.

  4. 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.
  5. 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 the dspOptions 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 the sendBlocking 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 the useCheckSum parameter to 1 only if you feel that the network is highly unreliable.

  6. Call the dspOpen routine to open the connection. The dspOpen routine has four possible modes of operation: ocAccept, ocEstablish, ocRequest, and ocPassive. Normally you use either the ocRequest or ocPassive mode. You must specify one of these four modes for the ocMode parameter when you call
    the dspOpen routine.

    The ocAccept mode is used only by connection servers. The ocEstablish 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 the dspOpen routine
    in the ocRequest 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 the filterAddress parameter to the dspOpen routine. When your connection end receives a response from a socket that meets the restrictions of the filterAddress 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 the dspOpen routine in the ocPassive 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 the filterAddress 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 the filterAddress 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 the noErr result code, then the connection is open.

  7. Use the dspRead routine to read data that your connection end has received from
    the remote connection end. Use the dspWrite routine to send data to the remote connection end. Use the dspAttention 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 the dspWrite 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 the flush parameter to the dspWrite routine does not guarantee that the send queue is empty. You can use
      the dspStatus 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 the dspRead routine should then
      call the dspRead 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 the dspRead 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 the actCount parameter, and returns the eom 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 the actCount 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 the noErr result code with the actCount parameter set to 0 and the eom parameter set to 0.

      In addition to the byte-stream data format implemented by the dspRead and dspWrite routines, ADSP provides a mechanism for sending and receiving control signals or information separate from the byte stream. You use the dspAttention 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 the eAttention flag in the userFlags field of the CCB and calls your user routine. Your user routine must first clear the userFlags 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.

  8. When you are ready to close the ADSP connection, you can use the dspClose or dspRemove routine to close the connection end. Use the dspClose 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 the dspRemove 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 the dspRemove routine. You can
    use the dspStatus routine to determine whether any data is remaining in the receive queue, or you can read data from the receive queue until both the actCount and
    eom fields of the dspRead parameter block return 0.

    If you set the abort parameter for the dspClose or dspRemove 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 the abort 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.

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 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 the userRoutine parameter to the dspInit function is shown in Listing 5-3 on page 5-28.) Next, Listing 5-1 uses the dspOptions 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 the dspOpen 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 the dspRemove 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}

Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996