|
|
Both XTI and sockets support two distinct types of service: connection oriented and connectionless.
When creating a socket, the type of service must be specified (for example, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW). The service type determines whether connection-oriented or connectionless semantics are used. For a simple example of connection establishment, consider the client side of a stream-oriented application:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h>The client must initiate a connection by first creating a stream socket and then using the connect call to establish communication with a preexisting socket on a server machine.main(argc, argv) /* client side of stream-oriented application */ int argc; char *argv[]; { int sock; struct sockaddr_in server; struct hostent *hp, *gethostbyname(); struct servent *sp, *getservbyname();
/* create socket */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("opening stream socket"); exit(1); } /* connect socket using name specified by command line */ server.sin_len = sizeof(server); server.sin_family = AF_INET; hp = gethostbyname(argv[1]); if (hp == 0) { fprintf(stderr, "%s: unknown host\n", argv[1]); exit(2); } memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); sp = getservbyname(argv[2], "tcp"); if (sp == 0) { fprintf(stderr, "%s: unknown service\n", argv[2]); exit(3); } server.sin_port = sp -> s_port;
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("connecting stream socket"); exit(1); } }
Notice the calls to gethostbyname and getservbyname. These are the socket-oriented network directory services described under ``Socket-based datagrams''. They take a host and service name, respectively, and return the host network address and the service port. The service port number can be thought of as a machine-specific service address. Certain well-known services are assumed to have specific TCP port numbers in the 1-to-1023 range. Some applications hard code these port numbers rather than using getservbyname.
When porting sockets applications to XTI, calls to gethostbyname and getservbyname, as well as hard-coded TCP port numbers, should be replaced by calls to the netdir_getbyname routine.
If the target socket exists and is prepared to handle a connection, the connection will complete successfully and the program can begin to send messages. Messages will be delivered in order without message boundaries. The connection is destroyed when both sockets are closed.
The XTI connection mode transport service is also circuit (stream) oriented, enabling data to be transferred over an established connection in a reliable, sequenced manner. Typical XTI client code is shown below:
#include <stdio.h> #include <netdir.h> #include <netconfig.h> #include <xti.h>Network selection is used by XTI applications to find the device file associated with the requested transport protocol. The device file that matches the protocol is passed to t_open. t_open then returns a file descriptor that identifies a new transport endpoint, and optionally (by way of its third argument), the default characteristics of the transport provider associated with that endpoint (and indirectly specified by the first argument). t_bind then binds the new transport endpoint to the transport address contained in its second argument. The typical client doesn't care what its own address is because no other process will try to access it. The second and third arguments in the example are therefore NULL.main() /* XTI client code */ { int fd; struct netconfig *nconf; void *handlep;
/* * select an appropriate network */
if ((handlep = setnetpath()) == NULL) { nc_perror("Error in initializing networks"); exit(1); }
/* * try all transports until finding one that matches * the users stated preferences */
while ((nconf = getnetpath(handlep)) != NULL) { if (nconf->nc_semantics == NC_TPI_COTS) break; }
if (nconf == NULL) { fprintf(stderr, "no transports available\n"); exit(1); }
if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) < 0) { t_error("t_open failed"); exit(2); }
if (t_bind(fd, NULL, NULL) < 0) { t_error("t_bind failed"); exit(3); }
endnetpath(handlep); }
Connection establishment for a server process is slightly different. The process must bind itself to an address and wait for clients to connect to it. The following code shows how a sockets server is bound to its known address:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h>In the example, the server explicitly asks to be bound to port SRV_PORT.#define SRV_PORT 2
main(argc,argv) /* sockets server */ int argc; char *argv[]; { int sock; struct sockaddr_in server; int msgsock; char buf[BUFSIZ];
/* create socket */
sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("opening stream socket"); exit(1); }
/* name socket */
server.sin_len = sizeof(server); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = SRV_PORT; if (bind(sock, (struct sockaddr *)&server), sizeof(server)) < 0) { perror("binding stream socket"); exit(2); }
/* the server now does a listen and accept */
}
The equivalent code for a XTI server process is shown below:
#include <stdio.h> #include <fcntl.h> #include <netconfig.h> #include <netdir.h> #include <xti.h>The examples show a significant difference between sockets and XTI. Since XTI server applications work over any transport provider, they use the Network Selection and Name-to-address Mapping features in order to be protocol independent; sockets applications use fixed addresses.#define SRV_ADDR 2
main(argc,argv) /* XTI server */ int argc; char *argv[]; { struct nd_hostserve hostserv; int fd; char buf[BUFSIZ]; struct netconfig *nconf; struct t_bind *bind; void *handlep;
if ( argc != 3 ) { fprintf(stderr, "USAGE: %s host service\n", argv[0]); exit(1); } hostserv.h_host = argv[1]; hostserv.h_serv = argv[2];
if ((handlep = setnetpath()) == NULL) { nc_perror("setnetpath"); exit(2); } /* * select an appropriate transport and * get address for remote host/service */ while ((nconf = getnetpath(handlep)) != NULL) { if (nconf->nc_semantics == NC_TPI_COTS) break; }
if (nconf == NULL) { fprintf(stderr, "no connection mode transport\n"); exit(3); }
if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) < 0) { t_error("t_open failed"); fprintf(stderr, "unable to open %s\n", nconf->nc_device); exit(4); } endnetpath(handlep);
if ((bind = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL)) == NULL) { t_error("t_alloc of t_bind structure failed"); exit(5); }
/* * for simplicity in this example, assume the * address is an integer */
bind->qlen = 1; bind->addr.len = sizeof(int); *(int *)bind->addr.buf = SRV_ADDR;
if (t_bind(fd, bind, bind) < 0) { t_error("t_bind failed"); exit(6); }
/* the server now does a listen and accept */
}