The following rules apply only to the connection-mode transport
The state diagram in
Active User | Passive User | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
t_open() | t_open()
t_bind()
|
| t_bind()
|
|
| t_listen()
| t_connect()
|
|
|
|
| t_accept()
| t_rcvconnect()
|
|
| t_snd()
|
|
| t_sndv()
|
|
|
|
| t_rcv()
|
|
| t_rcvv()
| t_snddis()
|
|
|
|
| t_rcvdis()
| t_unbind()
|
| t_unbind()
| t_close()
|
| t_close()
| |
The state diagram that follows shows the flow of the events through
the various states.
This example shows a successful exchange of data between user A and
user B.
For a detailed description of all possible states and events,
see
User A | User B | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
t_open() | t_open()
t_bind()
|
| t_bind()
| t_sndudata()
|
|
|
|
| t_rcvudata()
| t_unbind()
|
| t_unbind()
| t_close()
|
| t_close()
| |
These guidelines provide information additional to that given in
For applications to use XTI in a fully asynchronous manner, they will need
to use the facilities of an Event Management (EM) Interface.
Such an EM will
In the same way, the EM mechanism should allow the application to be notified of events coming from external sources, such as:
When handling multiple transport connections, the application could either:
or:
The application will have to maintain an appropriate balance and choose the right trade-off between the number of processes and the number of connections managed per process in order to minimise the resulting overhead.
Unfortunately, the system facilities to suspend and await notification of an event are presently system-dependent, although work is in progress within standards bodies to provide a unified and portable mechanism.
Hence, for the foreseeable future, applications could use whatever underlying system facilities exist for event notification.
Many vendors currently provide either the System V
Given the fact that a transport endpoint identifying a transport connection
maps to a file descriptor, applications can take advantage of such
EM mechanisms offered by the system (for example,
Guidelines for the use of
The XTI events can be divided into two classes of events.
T_LISTEN | Connect request indication. |
T_CONNECT | Connect response indication. |
T_DATA | Reception of normal data indication. |
T_EXDATA | Reception of expedited data indication. |
T_DISCONNECT | Disconnection request indication. |
T_ORDREL | Orderly release request indication. |
T_UDERR | Notification of an error in a previously sent datagram. |
This class of events should always be monitored by the application.
T_GODATA | Normal data may be sent again. |
T_GOEXDATA | Expedited data may be sent again. |
This class of events informs the application that flow control restrictions have been lifted on a given file descriptor.
The application should request to be notified of this class of events
whenever a flow control restriction has previously occurred on this endpoint
(for example, [TFLOW] error has been returned on a
Note that this class of event should not be monitored systematically otherwise the application would be notified each time a message is sent.
/*
* This is a simple server application example to show how poll() can
* be used in a portable manner to wait for the occurrence of XTI events.
* In this example, poll() is used to wait for the events T_LISTEN,
* T_DISCONNECT, T_DATA and T_GODATA.
*
* A transport endpoint is opened in asynchronous mode over a
* message-oriented transport provider (for example, ISO). The endpoint
* is bound with qlen = 1 and the application enters an endless loop
* to wait for all incoming XTI events on all its active endpoints.
* For all connection indications received, a new endpoint is opened
* with qlen = 0 and the connection request is accepted on that endpoint.
* For all established connections, the application waits for data
* to be received from one of its clients, sends the received data
* back to the sender and waits for data again.
* The cycle repeats until all the connections are released by
* the clients. The disconnection indications are processed and the
* endpoints closed.
*
* The example references two fictitious functions:
*
* - int get_provider(int tpid, char * tpname)
* Given a number as transport provider id, the function returns in
* tpname a string as transport provider name that can be used with
* t_open(). This function hides the different naming schemes of
* different XTI implementations.
*
* - int get_address(char * symb_name, struct netbuf address)
* Given a symbolic name symb_name and a pointer to a struct netbuf
* with allocated buffer space as input, the function returns a
* protocol address. This function hides the different addressing
* schemes of different XTI implementations.
*/
/*
* General Includes
*/
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <xti.h>
/*
* Include files for poll()
*/
#include <poll.h>
/*
* Various Defines
*/
/*
* The XTI events T_CONNECT, T_DISCONNECT, T_LISTEN, T_ORDREL and T_UDERR
* are related to one of the poll flags in INEVENTS (to which one, depends
* on the implementation). POLLOUT means that (at least) normal data may
* be sent, and POLLWRBAND that expedited data may be sent.
*/
#define ERREVENTS
(POLLERR | POLLHUP | POLLNVAL)
#define INEVENTS
(POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
#define OUTEVENTS
(POLLOUT | POLLWRBAND)
#define MY_PROVIDER
1
/*
transport provider id
*/
#define MAXSIZE
4000
/*
size of send/receive buffer
*/
#define TPLEN
30
/*
maximum length of provider name
*/
#define MAXCNX
10
/*
maximum number of connections
*/
extern int errno;
/*
* Declaration of non-integer external functions
*/
void exit();
void perror();
/* ============================================================== */
main()
{
register int
i;
/*
loop variable
*/
register int
num;
/*
return value of t_snd()
*/
/*
and t_rcv()
*/
int
discflag = 0;
/*
flag to indicate a
*/
/*
disc indication
*/
int
errflag = 0;
/*
flag to indicate an error
*/
int
event;
/*
stores events returned
*/
/*
by t_look()
*/
int
fd;
/*
current file descriptor
*/
int
fdd;
/*
file descriptor
*/
/*
for t_accept()
*/
int
flags;
/*
used with t_rcv()
*/
char
*datbuf;
/*
current send/receive buffer
*/
unsigned int
act = 0;
/*
active endpoints
*/
struct t_info
info;
/*
used with t_open()
*/
struct t_bind
*preq;
/*
used with t_bind()
*/
struct t_call
*pcall;
/*
used with t_listen()
*/
/*
and t_accept()
*/
struct t_discon
discon;
/*
used with t_rcvdis()
*/
char
tpname[TPLEN];
/*
transport provider name
*/
char
buf[MAXCNX][MAXSIZE];
/*
send/receive buffers
*/
int
rcvdata[MAXCNX];
/*
amount of data
*/
/*
already received
*/
int
snddata[MAXCNX];
/*
amount of data already sent
*/
struct pollfd
fds[MAXCNX];
/*
used with poll()
*/
/*
* Get name of transport provider
*/
if (get_provider(MY_PROVIDER, tpname) == -1) {
perror(">>> get_provider failed");
exit(1);
}
/*
* Establish a transport endpoint in asynchronous mode
*/
if ((fd = t_open(tpname, O_RDWR | O_NONBLOCK, &info)) == -1) {
t_error(">>> t_open failed");
exit(1);
}
/*
* Allocate memory for the parameters passed with t_bind().
*/
if ((preq = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR)) == NULL) {
t_error(">>> t_alloc(T_BIND) failed");
t_close(fd);
exit(1);
}
/*
* Given a symbolic name ("MY_NAME"), get_address returns an address
* and its length in preq->addr.buf and preq->addr.len.
*/
if (get_address("MY_NAME", &(preq->addr)) == -1) {
perror(">>> get_address failed");
t_close(fd);
exit(1);
}
preq->qlen = 1; /* is a listening endpoint */
/*
* Bind the local protocol address to the transport endpoint.
* The returned information is discarded.
*/
if (t_bind(fd, preq, NULL) == -1) {
t_error(">>> t_bind failed");
t_close(fd);
exit(1);
}
if (t_free(preq, T_BIND) == -1) {
t_error(">>> t_free failed");
t_close(fd);
exit(1);
}
/*
* Allocate memory for the parameters used with t_listen.
*/
if ((pcall = (struct t_call *) t_alloc(fd, T_CALL, T_ALL)) == NULL) {
t_error(">>> t_alloc(T_CALL) failed");
t_close(fd);
exit(1);
}
/*
* Initialise entry 0 of the fds array to the listening endpoint.
* To be portable across different XTI implementations,
* register for INEVENTS and not for POLLIN.
*/
fds[act].fd = fd;
fds[act].events = INEVENTS;
fds[act].revents = 0;
rcvdata[act] = 0;
snddata[act] = 0;
act = 1;
/*
* Enter an endless loop to wait for all incoming events.
* Connect requests are accepted on new opened endpoints.
* The example assumes that data is first sent by the client.
* Then, the received data is sent back again and so on, until
* the client disconnects.
* Note that the total number of active endpoints (act) should
* at least be 1, corresponding to the listening endpoint.
*/
fprintf(stderr, "Waiting for XTI events...\n");
while (act > 0) {
/*
* Wait for any events
*
*/
if (poll(&fds, (size_t)act, (int) -1) == -1) {
perror(">>> poll failed");
exit(1);
}
/*
* Process incoming events on all active endpoints
*/
for (i = 0 ; i < act ; i++) {
if (fds[i].revents == 0)
continue; /* no event for this endpoint */
if (fds[i].revents & ERREVENTS) {
fprintf(stderr, "[%d] Unexpected poll events: 0x%x\n",
fds[i].fd, fds[i].revents);
continue;
}
/*
* set the current endpoint
* set the current send/receive buffer
*/
fd = fds[i].fd;
datbuf = buf[i];
/*
* Check for events
*/
switch((event = t_look(fd))) {
case T_LISTEN:
/*
* Must be a connection indication
*/
if (t_listen(fd, pcall) == -1) {
t_error(">>> t_listen failed");
exit(1);
}
/*
* If it will exceed the maximum number
* of connections that the server can handle,
* reject the connection indication.
*/
if (act >= MAXCNX) {
fprintf(stderr, ">>> Connection request rejected\n");
if (t_snddis(fd, pcall) == -1)
t_error(">>> t_snddis failed");
continue;
}
/*
* Establish a transport endpoint
* in asynchronous mode
*/
if ((fdd = t_open(tpname, O_RDWR | O_NONBLOCK, &info))
== -1) {
t_error(">>> t_open failed");
continue;
}
/*
* Accept connection on this endpoint.
* fdd no longer needs to be bound,
* t_accept() will do it.
*/
if (t_accept(fd, fdd, pcall) == -1) {
t_error(">>> t_accept failed");
t_close(fdd);
continue;
}
fprintf(stderr, "Connection [%d] opened\n", fdd);
/*
* Register for all flags that might indicate
* a T_DATA or T_DISCONNECT event, i. e.,
* register for INEVENTS (to be portable
* through all XTI implementations).
*/
fds[act].fd = fdd;
fds[act].events = INEVENTS;
fds[act].revents = 0;
rcvdata[act] = 0;
snddata[act] = 0;
act++;
break;
case T_DATA:
/*
* Must be a data indication
*/
if ((num = t_rcv(fd, (datbuf + rcvdata[i]),
(MAXSIZE - rcvdata[i]), &flags)) == -1) {
switch (t_errno) {
case TNODATA:
/* No data is currently
* available: repeat the loop
*/
continue;
case TLOOK:
/* Must be a T_DISCONNECT event:
* set discflag
*/
event = t_look(fd);
if (event == T_DISCONNECT) {
discflag = 1;
break;
}
else
fprintf(stderr, "Unexpected event %d\n",
event);
default:
/* Unexpected failure */
t_error(">>> t_rcv failed");
fprintf(stderr, "connection id: [%d]\n", fd);
errflag = 1;
break;
}
}
if (discflag || errflag)
/* exit from the event switch */
break;
fprintf(stderr, "[%d] %d bytes received\n", fd, num);
rcvdata[i] += num;
if (rcvdata[i] < MAXSIZE)
continue;
if (flags & T_MORE) {
fprintf(stderr, "[%d] TSDU too long for receive
buffer\n", fd);
errflag = 1;
break; /* exit from the event switch */
}
/*
* Send the data back:
* Repeat t_snd() until either the whole TSDU
* is sent back, or an event occurs.
*/
fprintf(stderr, "[%d] sending data back\n", fd);
do {
if ((num = t_snd(fd, (datbuf + snddata[i]),
(MAXSIZE - snddata[i]), 0)) == -1) {
switch (t_errno) {
case TFLOW:
/*
* Register for the flags
* OUTEVENTS to get awaken by
* T_GODATA, and for INEVENTS
* to get aware of T_DISCONNECT
* or T_DATA.
*/
fds[i].events |= OUTEVENTS;
continue;
case TLOOK:
/*
* Must be a T_DISCONNECT event:
* set discflag
*/
event = t_look(fd);
if (event == T_DISCONNECT) {
discflag = 1;
break;
}
else
fprintf(stderr, "Unexpected event %d\n",
event);
default:
t_error(">>> t_snd failed");
fprintf(stderr, "connection id: [%d]\n", fd);
errflag = 1;
break;
}
}
else {
snddata[i] += num;
}
} while (MAXSIZE > snddata[i] && !discflag && !errflag);
/*
* Reset send/receive counters
*/
rcvdata[i] = 0;
snddata[i] = 0;
break;
case T_GODATA:
/*
* Flow control restriction has been lifted
* restore initial event flags
*/
fds[i].events = INEVENTS;
continue;
case T_DISCONNECT:
/*
* Must be a disconnection indication
*/
discflag = 1;
break;
case -1:
/*
* Must be an error
*/
t_error(">>> t_look failed");
errflag = 1;
break;
default:
/*
* Must be an unexpected event
*/
fprintf(stderr, "[%d] Unexpected event %d\n", fd, event);
errflag = 1;
break;
} /* end event switch */
if (discflag) {
/*
* T_DISCONNECT has been received.
* User data is not expected.
*/
if (t_rcvdis(fd, &discon) == -1)
t_error(">>> t_rcvdis failed");
else
fprintf(stderr, "[%d] Disconnection reason: 0x%x\n",
fd, discon.reason);
}
if (discflag || errflag) {
/*
* Close transport endpoint and
* decrement number of active connections
*/
t_close(fd);
act--;
/* Move last entry of fds array to current slot,
* adjust internal counters and flags
*/
fds[i].events = fds[act].events;
fds[i].revents = fds[act].revents;
fds[i].fd = fds[act].fd;
discflag = 0; /* clear disconnection flag */
errflag = 0; /* clear error flag */
i--; /* Redo the for() event loop to consider
* events related to the last entry of
* fds array */
fprintf(stderr, "Connection [%d] closed\n", fd);
}
} /* end of for() event loop */
} /* end of while() loop */
fprintf(stderr, ">>> Warning: no more active endpoints\n");
exit(1);
}
#define MY_PROVIDER
1
/*
transport provider id
*/
#define MAXSIZE
4000
/*
size of send/receive buffer
*/
#define TPLEN
30
/*
maximum length of provider name
*/
#define MAXCNX
10
/*
maximum number of connections
*/
extern int errno;
register int
i;
/*
loop variable
*/
register int
num;
/*
return value of t_snd()
*/
/*
and t_rcv()
*/
int
discflag = 0;
/*
flag to indicate a
*/
/*
disc indication
*/
int
errflag = 0;
/*
flag to indicate an error
*/
int
event;
/*
stores events returned
*/
/*
by t_look()
*/
int
fd;
/*
current file descriptor
*/
int
fdd;
/*
file descriptor
*/
/*
for t_accept()
*/
int
flags;
/*
used with t_rcv()
*/
char
*datbuf;
/*
current send/receive
*/
/*
buffer
*/
size_t
act = 0;
/*
active endpoints
*/
struct t_info
info;
/*
used with t_open()
*/
struct t_bind
*preq;
/*
used with t_bind()
*/
struct t_call
*pcall;
/*
used with t_listen()
*/
/*
and t_accept()
*/
struct t_discon
discon;
/*
used with t_rcvdis()
*/
char
tpname[TPLEN];
/*
transport provider name
*/
int
fds[MAXCNX];
/*
array of file descriptors
*/
char
buf[MAXCNX][MAXSIZE]
/*
send/receive buffers
*/
int
rcvdata[MAXCNX];
/*
amount of data
*/
/*
already received
*/
int
snddata[MAXCNX];
/*
amount of data already sent
*/
fd_set rfds, wfds, xfds;
/*
file descriptor sets
*/
/*
for select()
*/
fd_set rfdds, wfdds, xfdds;
/*
initial values of
*/
/*
file descriptor sets
*/
/*
rfds, wfds and xfds
*/
Contents | Next section | Index |