Programming with sockets

Connection mode

Both XTI and sockets support two distinct types of service: connection oriented and connectionless.

Establishing socket connections: client code

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>

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); } }

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.

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.

NOTE: Some transports hold the connection open briefly in case more data is sent. The user may also have directed the system to wait. For more information, see the discussion of the SO_LINGER option on the getsockopt(S) manual page.

Establishing XTI connections: client code

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>

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); }

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.

Establishing socket connections: server code

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>

#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 */


In the example, the server explicitly asks to be bound to port SRV_PORT.

Establishing XTI connections: server code

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>

#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 */


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.
© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005