![Content starts here](http://welcome.hp-ww.com/img/s.gif) |
VMS DECwindows Transport Manual
- Determine which free queue this XTCB came
from, based on the XTCB$B_SUBTYPE field of the XTCB.
- Store the address of the connection
structure in the XTCB and then initiate an asynchronous read $QIO into
the XTCB. The FREE_INPUT_AST read-completion AST routine is called with
the address of the XTCB when the I/O operation completes. The
FREE_INPUT_AST routine is described in Section 8.2.8.
- If the $QIO service failed, perform failure
recovery. Return the XTCB to the correct free queue, mark the
connection as dying, and store the failure status code in the XTCC with
the XTCC_STATUS macro.
In either case, return the result of the
$QIO service as the return value.
8.2.8 Sample FREE_INPUT_AST Routine
The FREE_INPUT_AST routine is the $QIO read-completion AST routine.
Note
In prior versions of the DECwindows transport-common layer, each
specific transport had to perform its own read-completion processing.
The transport-common DECW$XPORT_READ_COMPLETE routine has been
implemented to perform read-completion processing.
Specific transports that support an AST interface should call the
transport-common DECW$XPORT_READ_COMPLETE routine to perform the read
completion and initiate additional reads. See the description of the
DECW$XPORT_READ_COMPLETE routine in Chapter 5 for more informaton.
|
Example 8-8
shows a sample implementation of the FREE_INPUT_AST routine.
Example 8-8 Sample FREE_INPUT_AST Routine |
.
.
.
ROUTINE free_input_ast( xtcb : REF $BBLOCK ) : NOVALUE =
BEGIN
BIND
iosb = xtcb [xtcb$w_iosb] : VECTOR [4,WORD,UNSIGNED],
tcc = .xtcb [xtcb$l_rflink] : $BBLOCK ;
LOCAL
itcc : REF $BBLOCK,
status : INITIAL(.iosb [0]) ;
(1)VALIDATE_XTCC( tcc, itcc ) ;
(2)xtcb [xtcb$l_length] = .iosb [1] ;
(3)status = DECW$XPORT_READ_COMPLETE(.itcc, .xtcb, .status) ;
RETURN ;
END ;
.
.
.
|
- The connection-context address is stored in
the XTCB, which is user-modifiable. Call the VALIDATE_XTCC macro to
confirm that the address is uncorrupted and to get the address of the
IXTCC. If the XTCC ID is known and is valid, VALIDATE_XTCC returns the
previously registered address of the IXTCC data structure in the
ixtcc argument.
- The number of bytes read by the
just-completed I/O is in the second word of the IOSB (.iosb [1]). Use
this number to set the length field of the XTCB.
DECW$XPORT_READ_COMPLETE requires this field to be valid.
- Call DECW$XPORT_READ_COMPLETE to place the
XTCB on the input work queue. If a free buffer is available,
DECW$XPORT_READ_COMPLETE calls the routine identified by the
XTFT$A_FREE_INPUT_BUFFER field in the XTFT to start the next read.
8.2.9 Sample PARSE_INTERNET_ADDRESS Routine
The PARSE_INTERNET_ADDRESS routine converts an ASCII Internet Standard
Format address (nnn.nnn.nnn.nnn) into the binary, 32-bit
Network Standard Format address. This entails converting numeric fields
separated by periods into 1-byte values and then packing these bytes
into a longword with the first-encountered field converted and inserted
into the rightmost byte of the longword. For example, the address
130.180.40.44 would be converted to the longword value
2C28B48216.
If there are too few fields or any nonnumeric characters seen (other
than the field separator), then the routine returns a bad parameter
status. If the conversion is successful, the Network Standard Format
address is built in the sockaddrin structure.
Example 8-9 shows a sample implementation of the
PARSE_INTERNET_ADDRESS routine.
Example 8-9 Sample PARSE_INTERNET_ADDRESS |
.
.
.
ROUTINE parse_internet_address( str_desc: REF $BBLOCK [DSC$C_S_BLN],
address: REF VECTOR [4,BYTE,UNSIGNED] ) =
BEGIN
LOCAL
string : REF VECTOR [,BYTE],
strlen,
p ;
(1)string = .str_desc [DSC$A_POINTER] ;
strlen = .str_desc [DSC$W_LENGTH] ;
p = 0 ;
INCR i FROM 0 TO 3
DO
BEGIN
(2)address [.i] = 0 ;
(3)IF .strlen EQL 0
THEN
RETURN SS$_BADPARAM ;
IF .string [.p] EQL %C'.'
THEN
RETURN SS$_BADPARAM ;
(4)WHILE ( .string [.p] GEQU %C'0' ) AND ( .string [.p] LEQU %C'9' )
DO
BEGIN
address [.i] = .address [.i] * 10 + ( .string [.p] - %C'0' ) ;
p = .p + 1 ;
strlen = .strlen - 1 ;
IF .strlen EQL 0
THEN
EXITLOOP ;
END ;
(5)IF .strlen NEQ 0
THEN
BEGIN
IF .string [.p] NEQ %C'.'
THEN
RETURN SS$_BADPARAM ;
p = .p + 1 ;
strlen = .strlen - 1 ;
IF .strlen EQL 0
THEN
RETURN SS$_BADPARAM ;
END;
END ;
RETURN SS$_NORMAL ;
END ;
.
.
.
|
- Initialize the string variables to point to
the beginning of the string.
- Initialize the next address byte to zero.
- Make sure each number contains at least one
digit.
- Accumulate each digit until we reach a
nondigit.
- Make sure the numbers terminate in a dot
(except the last number).
8.2.10 Sample XTFT$A_CLOSE Routine
The XTFT$A_CLOSE routine initiates a series of operations that
disconnect a connection and release the data structures associated with
the link. Example 8-10 shows a sample implementation of the
XTFT$A_CLOSE routine.
Example 8-10 Sample XTFT$A_CLOSE Routine |
.
.
.
GLOBAL ROUTINE DECW$$TCPIP_CLOSE( itcc : REF $BBLOCK VOLATILE) =
BEGIN
LOCAL
tcc : REF $BBLOCK INITIAL( .itcc [ixtcc$a_tcc] ),
status ;
(1)tcc [xtcc$v_dying] = 1 ;
(2)$CANCEL( CHAN = .itcc [ixtcc$w_chan] ) ;
$DASSGN( CHAN = .itcc [ixtcc$w_chan] ) ;
itcc [ixtcc$w_chan] = 0 ;
(3)status = $DCLAST( ASTADR = close_and_deallocate_ast,
ASTPRM = .itcc ) ;
.status
END ;
.
.
.
|
- Mark the connection as dying, both to
prevent the caller from requesting operations on this connection and to
prevent the various completion AST routines from attempting to perform
additional work.
- Cancel I/O and deassign the channel to the
connection. This action completes all outstanding I/O operations to the
connection and queues all completion ASTs. Further references to the
channel are not permitted.
- Declare an AST to the
CLOSE_AND_DEALLOCATE_AST routine that is executed after the completion
ASTs. This routine performs the final cleanup operations such as
structure invalidation and deallocation.
8.2.11 Sample CLOSE_AND_DEALLOCATE_AST Routine
The CLOSE_AND_DEALLOCATE_AST routine completes the connection close
initiated by XTFT$A_CLOSE. Once this procedure executes, it is assumed
that neither the transport caller nor any part of the transport will
refer to this connection again. It is also assumed that all
XTCBs have been returned to the communication queue structure (XTCQ).
Example 8-11 shows a sample implementation of the
CLOSE_AND_DEALLOCATE_AST routine.
Example 8-11 Sample CLOSE_AND_DEALLOCATE_AST
Routine |
.
.
.
ROUTINE close_and_deallocate_ast( itcc : REF $BBLOCK ) : NOVALUE =
BEGIN
BUILTIN
REMQUE ;
LOCAL
tdb : REF $BBLOCK INITIAL( .itcc [ixtcc$a_tdb] ),
tpb : REF $BBLOCK INITIAL( .itcc [ixtcc$a_tpb] ),
status ;
(1)REMQUE( .itcc, itcc ) ;
tdb [xtdb$l_ref_count] = .tdb [xtdb$l_ref_count] - 1 ;
(2)DECW$XPORT_DEALLOC_QUEUES( .itcc ) ;
(3)itcc [ixtcc$a_xport_table] = 0 ;
(4)DECW$XPORT_DEALLOC_PMEM( .itcc ) ;
DECW$XPORT_DEALLOC_PMEM( .tpb ) ;
END ;
.
.
.
|
- Remove the IXTCC from the IXTCC queue in the
XTDB and decrement the reference count in the XTDB.
- Deallocate the storage previously allocated
for the connection queues.
- Zero the IXTCC$A_XPORT_TABLE field in the
IXTCC to catch any subsequent references to the connection.
- Deallocate the IXTCC and XTPB connection
structures. At this point, the connection is completely run down.
8.2.12 Sample XTFT$A_OPEN Routine
The XTFT$A_OPEN routine is invoked in executive mode to establish a
connection to an X server.
Connections are usually established in inner mode. However, if your
specific transport indicates (by an explicit return status of
DECW$_STALL) that it is necessary to wait for a connection to be
established across a network, DECW$XPORT_OPEN can wait in user mode for
the open to complete. Waiting in user mode allows a user to press
Ctrl/Y to exit the application in the event of a problem.
You can write your transport-specific XTFT$A_OPEN routine to either
wait in executive mode (via a $QIOW call) or in user mode (via an
asynchronous $QIO call) and use the return status of DECW$_STALL to
tell the DECW$XPORT_OPEN routine which way to proceed. However, waiting
in executive mode is discouraged. This example waits in user mode.
XTFT$A_OPEN uses three executive-mode ASTs to complete the connection
sequence asynchronously. The last $QIO AST completion routine,
OPEN_AST3, calls the transport-common DECW$$XPORT_OPEN_COMPLETE routine.
When DECW$XPORT_OPEN resumes, it drops back to user mode and then
checks the status returned by XTFT$A_OPEN, as follows:
- If XTFT$A_OPEN does return DECW$_STALL, DECW$XPORT_OPEN knows that
the connection is not completely set up and calls the $SYNCH system
service to wait. DECW$XPORT_OPEN uses an event flag and IOSB for this
purpose.
Note
If your transport layer opens connections asynchronously, it is
possible that multiple connection requests could be in progress at one
time; that is, while one connection is waiting to complete, another
connection could be initiated. If your application-specific
routines were to allocate connection-specific variables, such as the
IOSB used to hold the connection status, in an OWN program section,
those variables could be overwritten by another connection. To
prevent overwriting, the example XTFT$A_OPEN routine uses space on the
IXTCC data structure to hold connection-specific variables such as the
IOSB. Your specific transport can use the
extra_context_length and
extra_context_address arguments of the
DECW$XPORT_ALLOC_INIT_QUEUES routine to allocate additional space. It
is recommended that the IXTCC$Q_XPORT_RESERVED field
be used to point to data in this area.
|
When the connection is completely established,
DECW$$XPORT_OPEN_COMPLETE sets the event flag and the IOSB to complete
the wait.
- If XTFT$A_OPEN does not return DECW$_STALL but does not
return a failure status, DECW$XPORT_OPEN continues. This code path
allows DECW$XPORT_OPEN to handle the case where XTFT$A_OPEN completes
synchronously and therefore does not return DECW$_STALL.
For either case, as long XTFT$A_OPEN did not fail, DECW$XPORT_OPEN
marks the connection as active in the XTCC.
Example 8-12 shows a sample implementation of the XTFT$A_OPEN routine.
Example 8-12 Sample DECW$$TCPIP_OPEN
Routine |
.
.
.
GLOBAL ROUTINE DECW$$TCPIP_OPEN( workstation : REF $BBLOCK, server, itcc : REF
$BBLOCK ) =
BEGIN
BUILTIN
REMQUE,
INSQUE ;
BIND
tpb = .itcc [ixtcc$a_tpb] : $BBLOCK ;
LOCAL
socktype : INITIAL( (UCX$C_STREAM ^ 16) + UCX$C_TCP ),
status,
saved_wkstn_space,
tcc : REF $BBLOCK,
saved_wkstn_name : REF $BBLOCK,
sockaddrin : $BBLOCK [SIN$S_SOCKADDRIN] PRESET(
[SIN$W_FAMILY] = INET$C_AF_INET,
(1)[SIN$W_PORT] = 0,
[SIN$L_ADDR] = swap_long( INET$C_INADDR_ANY ) ),
sin_desc : VECTOR [2] INITIAL( %ALLOCATION( sockaddrin ), sockaddrin ),
LABEL
connect ;
itcc [ixtcc$l_server_number] = .server;
(2) connect:
BEGIN
BEGIN
!
! Check for node "0"
!
(3) IF .workstation [DSC$W_LENGTH] EQL 1 AND .(.workstation [DSC$A_POINTER])<0,8,0> EQL %C'0'
THEN
workstation = lnn_desc ;
!
! Allocate the user modifiable memory...
!
(4) status = DECW$XPORT_ALLOC_INIT_QUEUES( .itcc,
.tcpip_tft[xtft$l_xtcc_length],
.tpb [xtpb$w_srp_size],
.tpb [xtpb$w_lrp_size],
.tpb [xtpb$w_i_srp_count],
.tpb [xtpb$w_i_lrp_count],
.tpb [xtpb$w_o_srp_count],
.tpb [xtpb$w_o_lrp_count],
.workstation [DSC$W_LENGTH],
saved_wkstn_space) ;
IF NOT .status
THEN
RETURN .status ;
tcc = .itcc[ixtcc$a_tcc] ;
(5) INSQUE( .itcc, tcpip_tdb [xtdb$a_itcc_flink] ) ;
tcpip_tdb [xtdb$l_ref_count] = .tcpip_tdb [xtdb$l_ref_count] + 1 ;
(6) CH$MOVE( .workstation [DSC$W_LENGTH],
.workstation [DSC$A_POINTER],
.saved_wkstn_space ) ;
saved_wkstn_name = itcc [ixtcc$q_xport_reserved] ;
saved_wkstn_name [DSC$W_LENGTH] = .workstation [DSC$W_LENGTH] ;
saved_wkstn_name [DSC$A_POINTER] = .workstation [DSC$A_POINTER] ;
(7) IF NOT (status = $assign( DEVNAM = inet_dev_desc,
CHAN = itcc [ixtcc$w_chan],
ACMODE = psl$c_user ) )
THEN
LEAVE connect ;
(8) status = $QIO( EFN = .tcpip_tdb [xtdb$w_efn],
CHAN = .itcc [ixtcc$w_chan],
FUNC = IO$_SETMODE,
IOSB = itcc [ixtcc$q_iosb],
ASTADR = OPEN_AST1,
ASTPRM = .itcc,
P1 = socktype,
P2 = ( %X'01000000' OR INET$M_LINGER ),
P3 = sin_desc ) ;
(9) XPORT_FAO('Open $QIO status = !XL, iosb = !XL', .status,
.itcc [ixtcc$l_iosb]) ;
IF NOT .status
THEN
LEAVE connect ;
(10) RETURN DECW$_STALL ;
(11) END;
(12) IF .itcc [ixtcc$w_chan] NEQU 0
THEN
$DASSGN( CHAN = .itcc [ixtcc$w_chan] ) ;
REMQUE( .itcc, itcc ) ;
tcpip_tdb [xtdb$l_ref_count] = .tcpip_tdb [xtdb$l_ref_count] - 1 ;
RETURN .status ;
END ;
.
.
.
|
- Initialize the sockaddrin structure to
request an Internet-protocol socket on any available port.
- Start a named block of code (connect) that
attempts to allocate and initialize the resources needed to maintain a
connection. If any part of this setup fails, the code block exits and
failure processing recovers any allocated resources.
- If the transport caller requested a
connection with a node-identifying string of "0", use the Internet name
of the host as the node string.
- Call the transport-common
DECW$XPORT_ALLOC_INIT_QUEUES routine to allocate and initialize the
communication queues. DECW$XPORT_ALLOC_INIT_QUEUES allocates a block of
storage for an XTCC, XTCQ, and all of the XTCBs for a connection.
DECW$XPORT_ALLOC_INIT_QUEUES must allocate at least an XTCC; the other
structures are optional. DECW$XPORT_ALLOC_INIT_QUEUES places all of the
XTCBs on the appropriate free queues.
Allocate extra space on the
end of the IXTCC large enough to hold the workstation's name.
- Insert the IXTCC on the XTDB's queue of
active connections so that the structure can be found if the rundown
routine is invoked before the connection is fully started. Increment
the XTDB$L_REF_COUNT field that tracks the number of connections using
this transport.
- Save the workstation's name in the IXTCC so
that the subsequent OPEN _AST routines are able to use the name. Use
the IXTCC$Q_XPORT_RESERVED field as a string descriptor to the saved
workstation name.
- Assign a channel and create a socket to the
Internet networking service.
- Perform a SETMODE $QIO system service to
establish the desired characteristics on the socket.
ASTPRM is the
location of the preallocated IXTCC passed to the XTFT$A_OPEN routine.
The P1 argument specifies a stream-mode, TCP/IP
socket. The P2 argument enables the
"linger" option on the TCP/IP socket. The
P3 argument provides port, address, and address-family
information for the socket. If the $QIO completes successfully,
the OPEN_AST1 completion routine continues the connection setup.
- Call the XPORT_FAO macro to output the status
of the $QIO as a debugging check.
- Return the status DECW$_STALL to wait in user
mode for the open processing to complete. OPEN_AST1 executes in
executive mode when the $QIO completes.
- End of block named 'connect'.
- We reach here only if we encounter an error
during connection setup. Deassign the channel and remove this
connection from the list of known connections. The common layer
deallocates the user memory allocated by DECW$XPORT_ALLOC_INIT_QUEUES.
8.2.13 Sample OPEN_AST1 Routine
OPEN_AST1 is invoked in executive mode when the IO$_SETMODE $QIO issued
by the XTFT$A_OPEN routine completes. OPEN_AST1 continues processing to
establish the client connection.
Example 8-13 shows a sample implementation of the OPEN_AST1 completion
routine.
Example 8-13 Sample OPEN_AST1 Routine |
.
.
.
GLOBAL ROUTINE OPEN_AST1( itcc : REF $BBLOCK ) : NOVALUE =
BEGIN
BUILTIN
REMQUE;
LOCAL
net_addr_desc : $BBLOCK[DSC$S_DSCDEF1] PRESET (
[DSC$W_LENGTH] = ixtcc$s_server_addr - 1,
[DSC$B_CLASS] = DSC$K_CLASS_S,
[DSC$B_DTYPE] = DSC$K_DTYPE_T,
[DSC$A_POINTER] = itcc [ixtcc$t_server_addr] ),
func_code : INITIAL( INETACP_FUNC$C_GETHOSTBYNAME ),
func_code_desc : VECTOR [2] INITIAL( %ALLOCATION( func_code ), func_code ),
status ;
(1)status = .itcc [ixtcc$l_iosb] ;
IF .status
THEN
(2)IF (status = $qio( EFN = .tcpip_tdb [xtdb$w_efn],
CHAN = .itcc [ixtcc$w_chan],
FUNC = IO$_ACPCONTROL,
IOSB = itcc [ixtcc$q_iosb],
ASTADR = OPEN_AST2,
ASTPRM = .itcc,
P1 = func_code_desc,
P2 = itcc [ixtcc$q_xport_reserved],
P3 = itcc [ixtcc$l_server_addr_len],
P4 = net_addr_desc ) )
THEN
!
! Let OPEN_AST2 complete the connection setup.
!
(3)RETURN ;
(4)$DASSGN( CHAN = .itcc [ixtcc$w_chan] ) ;
REMQUE( .itcc, itcc ) ;
tcpip_tdb [xtdb$l_ref_count] = .tcpip_tdb [xtdb$l_ref_count] - 1 ;
DECW$$XPORT_OPEN_COMPLETE( .itcc, .status ) ;
RETURN;
END ;
.
.
.
|
- Check the status of the SETMODE $QIO
operation that just completed.
- Get the address of the remote server by
attempting to perform a name-to-address conversion with an
IO$_ACPCONTROL $QIO system service that queries the UCX host database.
The IO$_ACPCONTROL arguments are as follows:
- P1 specifies the function to be performed by the
ACPCONTROL $QIO, which in this case requests a get-host-by-name
conversion.
- P2 is the address of a descriptor of the host
name to search for in the host database.
- P3 is the address to receive the length of the
returned address string.
- P4 is the address of the descriptor of the
storage to receive the address found by the search.
If the $QIO completes successfully, the OPEN_AST2 AST completion
routine continues the connection setup.
- Dismiss the executive-mode AST. The process
will continue to wait in user mode.
- An error occurred, either during the SETMODE
$QIO or while checking the arguments to the get-host-by-name call.
Deassign the channel to UCX, remove the connection from the list of
known connections, and call the transport common
DECW$$XPORT_OPEN_COMPLETE routine with the failure status. The
transport-common layer deallocates the user memory allocated by
DECW$XPORT_ALLOC_INIT_QUEUES.
8.2.14 Sample OPEN_AST2 Routine
OPEN_AST2 is invoked in executive mode when the IO$_ACPCONTROL $QIO
issued by the OPEN_AST1 routine completes. OPEN_AST2 converts the text
result to an address and attempts to connect to the workstation.
Example 8-14 shows a sample implementation of the OPEN_AST2 completion
routine.
Example 8-14 Sample OPEN_AST2 Routine |
.
.
.
GLOBAL ROUTINE OPEN_AST2( itcc : REF $BBLOCK ) : NOVALUE =
BEGIN
BUILTIN
REMQUE ;
BIND
workstation = itcc [ixtcc$q_xport_reserved] : $BBLOCK[DSC$S_DSCDEF1] ;
LOCAL
status,
net_addr_desc : $BBLOCK[DSC$S_DSCDEF1] PRESET (
[DSC$W_LENGTH] = .itcc [ixtcc$l_server_addr_len],
[DSC$B_CLASS] = DSC$K_CLASS_S,
[DSC$B_DTYPE] = DSC$K_DTYPE_T,
[DSC$A_POINTER] = itcc [ixtcc$t_server_addr] ),
sockaddrin : $BBLOCK [SIN$S_SOCKADDRIN] PRESET(
[SIN$W_FAMILY] = INET$C_AF_INET ),
sin_desc : VECTOR [2] INITIAL( %ALLOCATION( sockaddrin ), sockaddrin ),
LABEL
connect;
connect:
BEGIN
(1)status = .itcc [ixtcc$l_iosb] ;
IF NOT .status
THEN
IF .status NEQU SS$_ENDOFFILE
THEN
LEAVE connect
(2)ELSE
BEGIN
IF .workstation [DSC$W_LENGTH] GEQU ixtcc$s_server_addr
THEN
BEGIN
status = DECW$_INVSRVNAM ;
LEAVE connect ;
END ;
CH$MOVE( .workstation [DSC$W_LENGTH],
.workstation [DSC$A_POINTER],
itcc [ixtcc$t_server_addr] ) ;
net_addr_desc [DSC$W_LENGTH] = .workstation [DSC$W_LENGTH] ;
END ;
(3)status = parse_internet_address( net_addr_desc,
sockaddrin [SIN$L_ADDR] );
IF NOT .status
THEN
BEGIN
status = DECW$_INVSRVNAM ;
LEAVE connect ;
END;
(4)sockaddrin [SIN$W_PORT] =
SWAP_SHORT( ( BASE_TCP_PORT + .itcc [ixtcc$l_server_number] ) ) ;
(5)status = $QIO( EFN = .tcpip_tdb [xtdb$w_efn],
CHAN = .itcc [ixtcc$w_chan],
FUNC = IO$_ACCESS,
IOSB = itcc [ixtcc$q_iosb],
ASTADR = OPEN_AST3,
ASTPRM = .itcc,
P3 = sin_desc ) ;
IF NOT .status
THEN
LEAVE connect ;
(6)RETURN ;
(7)END;
(8)$DASSGN( CHAN = .itcc [ixtcc$w_chan] ) ;
REMQUE( .itcc, itcc ) ;
tcpip_tdb [xtdb$l_ref_count] = .tcpip_tdb [xtdb$l_ref_count] - 1 ;
DECW$$XPORT_OPEN_COMPLETE( .itcc, .status ) ;
RETURN;
END ;
.
.
.
|
|