|
|
The connection establishment procedures highlight the distinction between clients and servers. The Transport Interface imposes a different set of procedures in this phase for each type of transport user. When the server is ready to accept connections from any client, it calls t_listen(S), which blocks until a connection indication is received. The client initiates the connection establishment to a particular server by calling t_connect(S). The server is then notified a connection indication has arrived, and the t_listen call returns. The server may either accept or reject the client's request. It will call t_accept(S) to establish the connection, or call t_snddis(S) to reject the request. The client will be notified of the server's decision when t_connect completes. For a successful connection to be established, the server should be executing a t_listen before the client issues the t_connect.
The Transport Interface supports two facilities during connection establishment that may not be supported by all transport providers:
The client may send data to the server when it requests a connection. This data will be passed to the server by t_listen. Similarly, the server can send data to the client when it accepts or rejects the connection. The connect characteristic returned by t_open determines how much data, if any, two users may transfer during connect establishment.
The client may specify protocol options that it would like the transport provider and/or the remote user to support. The Transport Interface supports both local and remote option negotiation. As discussed earlier, option negotiation is inherently a protocol-specific function. Use of this facility is discouraged if protocol independent software is a goal (see ``Guidelines for protocol independence'').
Continuing with the client/server example, the steps needed by the client to establish a connection are shown next:
/* By assuming that the address is an integer value, * this program may not run over another protocol. */ if ((sndcall = (struct t_call *)t_alloc(fd, T_CALL, T_ADDR)) == NULL) { t_error("t_alloc failed"); exit(3); } sndcall->addr.len = sizeof(int); *(int *)sndcall->addr.buf = SRV_ADDR;The t_connect call establishes the connection with the server. The first argument to t_connect identifies the transport endpoint through which the connection is established, and the second argument identifies the destination server. This argument is a pointer to a t_call structure with the following members:if (t_connect(fd, sndcall, NULL) < 0) { t_error("t_connect failed for fd"); exit(4); }
struct t_call { struct netbuf addr; struct netbuf opt; struct netbuf udata; int sequence; }
addr
identifies the address of the server,
opt
may be used to specify protocol-specific options that the client
would like to associate with the connection, and
udata
identifies user data that may be sent with the connect request to
the server.
The
sequence
member has no meaning for
t_connect.
t_alloc
is called to allocate the
t_call
structure dynamically.
Once allocated, the appropriate values are assigned.
In this example, no options or user data is associated with the
t_connect
call, but the server's address must be set.
The third argument to
t_alloc
is set to
T_ADDR
to specify that an appropriate
netbuf
buffer should be allocated for the address.
The server's address is then assigned to
buf
,
and
len
is set accordingly.
The third argument to t_connect can be used to return information about the newly established connection to the user, and may retrieve any user data sent by the server in its response to the connect request. It is set to NULL by the client here to show that this information is not needed. The connection will be established on successful return of t_connect. If the server rejects the connect request, t_connect will fail and set t_errno to TLOOK.
The TLOOK error has special significance in the Transport Interface. TLOOK notifies the user if a Transport Interface routine is interrupted by an unexpected asynchronous transport event on the given transport endpoint. As such, TLOOK does not report an error with a Transport Interface routine, but indicates that the normal processing of that routine will not be done because of the pending event. The events defined by the Transport Interface are listed here:
Returning to the example, when the client calls t_connect, a connect indication will be generated on the server's listening transport endpoint. The steps required by the server to process the event are discussed below. For each client, the server accepts the connect request and spawns a server process to manage the connection.
if ((call = (struct t_call *)t_alloc(listen_fd, T_CALL, T_ALL)) == NULL) { t_error("t_alloc of t_call structure failed"); exit(5); }The server will loop forever, processing each connect indication. First, the server calls t_listen to retrieve the next connect indication. When one arrives, the server calls accept_call to accept the connect request. accept_call accepts the connection on an alternate transport endpoint (as discussed below) and returns the value of that endpoint. conn_fd is a global variable that identifies the transport endpoint where the connection is established. Because the connection is accepted on an alternate endpoint, the server may continue listening for connect indications on the endpoint that was bound for listening. If the call is accepted without error, run_server will spawn a process to manage the connection.while (1) { if (t_listen(listen_fd, call) < 0) { t_error("t_listen failed for listen_fd"); exit(6); }
if ((conn_fd = accept_call(listen_fd, call)) != DISCONNECT) run_server(listen_fd); }
The server allocates a
t_call
structure to be used by
t_listen.
The third argument to
t_alloc,
T_ALL,
specifies that all necessary buffers should be allocated for retrieving
the caller's address, options, and user data.
As mentioned earlier, the transport provider in this example does
not support the transfer of user data during connection establishment,
and also does not support any protocol options.
Therefore,
t_alloc
will not allocate buffers for the user data and options.
It must, however, allocate a buffer large enough to store the address of
the caller.
t_alloc
determines the buffer size from the
addr
characteristic returned by
t_open.
The
maxlen
member of each
netbuf
structure will be set to the size of the newly allocated buffer by
t_alloc
(maxlen
is 0 for the user data and options buffers).
Using the t_call structure, the server calls t_listen to retrieve the next connect indication. If one is currently available, it is returned to the server immediately. Otherwise, t_listen will block until a connect indication arrives.
When a connect indication arrives, the server calls accept_call to accept the client's request, as follows:
accept_call(listen_fd, call) int listen_fd; struct t_call *call; { int resfd;accept_call takes two arguments.if ((resfd = t_open("/dev/ticots", O_RDWR, NULL)) < 0) { t_error("t_open for responding fd failed"); exit(7); }
if (t_bind(resfd, NULL, NULL) < 0) { t_error("t_bind for responding fd failed"); exit(8); }
if (t_accept(listen_fd, resfd, call) < 0) { if (t_errno == TLOOK) { /* must be a disconnect */ if (t_rcvdis(listen_fd, NULL) < 0) { t_error("t_rcvdis failed for listen_fd"); exit(9); } if (t_close(resfd) < 0) { t_error("t_close failed for responding fd"); exit(10); } /* go back up and listen for other calls */ return(DISCONNECT); } t_error("t_accept failed"); exit(11); } return(resfd); }
The server first establishes another transport endpoint by opening the clone device node of the transport provider and binding an address. As with the client, a NULL value is passed to t_bind to specify that the user does not care what address is bound by the provider. The newly established transport endpoint, resfd, is used to accept the client's connect request.
The first two arguments of t_accept specify the listening transport endpoint and the endpoint where the connection will be accepted, respectively. A connection may be accepted on the listening endpoint, but this prevents other clients from accessing the server for the duration of the connection.
The third argument of t_accept points to the t_call structure associated with the connect indication. This structure should contain the address of the calling user and the sequence number returned by t_listen. The value of sequence is significant if the server manages multiple outstanding connect indications. ``Advanced topics'' presents an example of this situation. Also, the t_call structure should identify protocol options the user would like to specify, and user data that may be passed to the client. Because the transport provider in this example does not support protocol options or the transfer of user data during connection establishment, the t_call structure returned by t_listen may be passed without change to t_accept.
For simplicity in the example, the server will exit if either the t_open or t_bind call fails. exit(S) will close the transport endpoint associated with listen_fd, causing the transport provider to pass a disconnect indication to the client that requested the connection. This disconnect indication notifies the client that the connection was not established; t_connect will fail, setting t_errno to TLOOK.
t_accept may fail if an asynchronous event has occurred on the listening transport endpoint before the connection is accepted, and t_errno will be set to TLOOK. The state transition table in ``State transitions'' shows that the only event that may occur in this state with only one outstanding connect indication is a disconnect indication. This event may occur if the client decides to undo the connect request it had previously sent. If a disconnect indication arrives, the server must retrieve the disconnect indication using t_rcvdis. This routine takes a pointer to a t_discon structure as an argument, which is used to retrieve information associated with a disconnect indication. In this example, however, the server does not care to retrieve this information, so it sets the argument to NULL.
After receiving the disconnect indication, accept_call closes the responding transport endpoint and returns DISCONNECT, which informs the server that the connection was disconnected by the client. The server then listens for further connect indications.
``Listening and responding transport endpoints'' illustrates how the server establishes connections.
Listening and responding transport endpoints
The transport connection is established on the newly created responding endpoint, and the listening endpoint is freed to retrieve further connect indications.