|
|
Once a user has bound an address to the transport endpoint, datagrams may be sent or received over that endpoint. Each outgoing message is accompanied by the address of the destination user. In addition, the Transport Interface enables a user to specify protocol options that should be associated with the transfer of the data unit (for example, transit delay). As discussed earlier, each transport provider defines the set of options, if any, that may accompany a datagram. When the datagram is passed to the destination user, the associated protocol options may be returned as well.
The following sequence of calls illustrates the data transfer phase of the connectionless-mode server:
if ((ud = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ALL)) == NULL) { t_error("t_alloc of t_unitdata structure failed"); exit(5); }The server must first allocate a t_unitdata structure for storing datagrams, which has the following format:if ((uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL)) == NULL) { t_error("t_alloc of t_uderr structure failed"); exit(6); }
while (1) { if (t_rcvudata(fd, ud, &flags) < 0) { if (t_errno == TLOOK) { /* * Error on previously sent datagram */ if (t_rcvuderr(fd, uderr) < 0) { exit(7); }
fprintf(stderr, "bad datagram, error = %d\n", uderr->error); continue; } t_error("t_rcvudata failed"); exit(8); } /* * query() processes the request and places the * response in ud->udata.buf, setting ud->udata.len */ query(ud);
if (t_sndudata(fd, ud, 0) < 0) { t_error("t_sndudata failed"); exit(9); } } }
query(ud) struct t_unitdata *ud; { /* Merely a stub for simplicity */
}
struct t_unitdata { struct netbuf addr; struct netbuf opt; struct netbuf udata; }
addr
holds the source address of incoming datagrams and the
destination address of outgoing datagrams,
opt
identifies any protocol options associated with the transfer of the
datagram, and
udata
holds the data itself.
The
addr
,
opt
,
and
udata
members must all be allocated with buffers large enough to hold
any possible incoming values.
As described in the previous section, the
T_ALL
argument to
t_alloc
will ensure this and will set the
maxlen
member of each
netbuf
structure accordingly.
Because the provider does not support protocol options in this
example, no options buffer will be allocated, and
maxlen
will be set to zero in the
netbuf
structure for options.
The server also allocates a
t_uderr
structure for processing any datagram errors, as discussed later
in this section.
The transaction server loops forever, receiving queries, processing the queries, and responding to the clients. It first calls t_rcvudata to receive the next query. t_rcvudata will retrieve the next available incoming datagram. If none is currently available, t_rcvudata will block, waiting for a datagram to arrive. The second argument of t_rcvudata identifies the t_unitdata structure in which the datagram should be stored.
The third argument,
flags ,
must point to an integer variable and may be set to
T_MORE
on return from
t_rcvudata
to specify that the user's
udata
buffer was not large enough to store the full datagram.
In this case, subsequent calls to
t_rcvudata
will retrieve the remainder of the datagram.
Because
t_alloc
allocates a
udata
buffer large enough to store the maximum datagram size, the
transaction server does not have to check the value of
flags.
If a datagram is received successfully, the transaction server calls the
query
routine to process the request.
This routine will store the response in the structure pointed to by
ud,
and will set
ud->udata.len
to specify the number of bytes in the response.
The source address returned by
t_rcvudata
in
ud->addr
will be used as the destination address by
t_sndudata.
When the response is ready, t_sndudata is called to return the response to the client. The Transport Interface prevents a user from flooding the transport provider with datagrams using the same flow control mechanism described for the connection-mode service. In such cases, t_sndudata will block until the flow control is relieved, and will then resume its operation.