[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


B    Socket and XTI Programming Examples

This appendix contains annotated files for a sample server/clientSection B.1, for each request received, the server program forks a child process to handle the request. The database information is "detached" in the child process' private data area. When the child process analyzes the request and reduces the customer's credit balance appropriately, it needs to update this information in the original server's data area (and on some persistent storage as well) so that the next request for the same customer is handled correctly. To avoid unnecessary complexity, this logic is not included in the program.

The information is organized as follows:

You can obtain copies of these example programs from /usr/examples/network_programming.


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


B.1    Connection-Oriented Programs

This section contains sockets and XTI variations of the same server and client programs, written for connection-oriented modes communication.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.1.1    Socket Server Program

Example B-1 implements a server using the socket interface.

Example B-1: Connection-Oriented Socket Server Program

/*
 *
 * This file contains the main socket server code
 * for a connection-oriented mode of communication.
 *
 * Usage:       socketserver
 *
 */

 
#include "server.h"
 
char *parse(char *); struct transaction *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[]) { int sockfd; int newsockfd; struct sockaddr_in serveraddr; struct sockaddr_in clientaddr; int clientaddrlen = sizeof(clientaddr); struct hostent *he; int pid;
 

 
signal(SIGCHLD, SIG_IGN);
 
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) [1] { perror("socket_create"); exit(1); }
 

 
bzero((char *) &serveraddr, sizeof(struct sockaddr_in)); [2] serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); [3] serveraddr.sin_port = htons(SERVER_PORT); [4]
 

 
if ( bind(sockfd, [5] (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)) < 0) { perror("socket_bind"); exit(2); }
 
listen(sockfd, 8); [6]
 
while(1) {
 
if ((newsockfd = accept(sockfd, [7] (struct sockaddr *) &clientaddr, &clientaddrlen)) < 0) { if (errno == EINTR) { printf("Bye...\n"); exit(0); } else { perror("socket_accept"); exit(3); } }
 
pid = fork();
 
switch(pid) { case -1: /* error */ perror("dosession_fork"); break; default: close(newsockfd); break; case 0: /* child */
 
close(sockfd); transactions(newsockfd);
 
close(newsockfd); return(0);
 
} }
 
}
 

 
transactions(int fd) [8] { int bytes; char *reply; int dcount; char datapipe[MAXBUFSIZE+1];
 
/* * Look at the data buffer and parse commands, * keep track of the collected data through * transaction_status. * */ while (1) { if ((dcount=recv(fd, datapipe, MAXBUFSIZE)) [9] < 0) { perror("transactions_receive"); break; } if (dcount == 0) { return(0); }
 
datapipe[dcount] = '\0';
 
if ((reply=parse(datapipe)) != NULL) { send(fd, reply, strlen(reply), 0); [10] } } }

  1. Create a socket with the socket call.

    AF_INET specifies the Internet communication domain. Alternatively, if OSI transport were supported, a corresponding constant such as AF_OSI would be required here. The socket type SOCK_STREAM is specified for TCP or connection-oriented communication. This parameter indicates that the socket is connection-oriented.

    Contrast the socket call with the t_open call in the XTI server example ( Section B.1.3). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP and UDP/IP this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family, which is AF_INET in this example. If AF_OSI were used instead of AF_INET, then sockaddr_osi would be required for the bind call instead of sockaddr_in. [Return to example]

  3. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), and ntohs(3).
    [Return to example]

  4. SERVER_PORT is defined in the common.h header file. It is a short integer, which helps identify the server process from other application processes. Numbers from 0 to 1024 are reserved. [Return to example]

  5. Bind the server's address to this socket with the bind call. The combination of the address and port number identify it uniquely on the network. [Return to example]

  6. Specify the number of pending connections the server can queue while it finishes processing the previous accept call. This value governs the success rate of connections while the server processes accept calls. Use a larger number to obtain a better success rate if multiple clients are sending the server connect requests. The operating system imposes a ceiling on this value. [Return to example]

  7. Accept connections on this socket. For each connection, the server forks a child process to handle the session to completion. The server then resumes listening for new connection requests. This is an example of a concurrent server. You can also have an iterative server, meaning that the server handles the data itself. See Section B.2 for an example of iterative servers. [Return to example]

  8. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided, such as the merchant's login ID, password, and customer's credit card number. This process is
    repeated until the parse function identifies a complete transaction and returns a response packet, to be sent to the client program.

    The client program can send information packets in any order (and in one or more packets), so the parse function is designed to remember state information sufficient to deal with this unstructured message stream.

    Since the program uses a connection-oriented protocol for data transfer, this function uses send and recv to send and receive messages, respectively. [Return to example]

  9. Receive data with the recv call. [Return to example]

  10. Send data with the send call. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.1.2    Socket Client Program

Example B-2 implements a client program that can communicate with the socketserver interface shown in Example B-1.

Example B-2: Connection-Oriented Socket Client Program

/*
 *
 * This generates the client program.
 *
 * usage: socketclient [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */

 

 
#include "client.h"
 
main(int argc, char *argv[]) { int sockfd; struct sockaddr_in serveraddr; struct hostent *he; int n; char *serverhost = "localhost"; struct hostent *serverhostp; char buffer[1024]; char inbuf[1024];
 

 
if (argc>1) { serverhost = argv[1]; }
 
init();
 
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) [1] { perror("socket_create"); exit(1); }
 

 
bzero((char *) &serveraddr, sizeof(struct sockaddr_in)); [2] serveraddr.sin_family = AF_INET;
 
if ((serverhostp = gethostbyname(serverhost)) == [3] (struct hostent *)NULL) { fprintf(stderr,"gethostbyname on %s failed\n", serverhost); exit(1); } bcopy(serverhostp->h_addr, (char *)&(serveraddr.sin_addr.s_addr), serverhostp->h_length);
 
serveraddr.sin_port = htons(SERVER_PORT); [4]
 
/* Now connect to the server */ if (connect(sockfd, &serveraddr, sizeof(serveraddr)) [5] < 0) { perror ("connect"); exit(2); }
 

 
while(1) { /* Merchant record */ sprintf(buffer, "%%%%m%s##%%%%p%s##", merchantname, password);
 
printf("\n\nSwipe card, enter amount: "); fflush(stdout); if (scanf("%s", inbuf) == EOF) { printf("bye...\n"); exit(0); } soundbytes();
 
sprintf(buffer, "%s%%%%a%s##%%%%n%s##", buffer, inbuf, swipecard());
 
if (send(sockfd, buffer, strlen(buffer), 0) [6] < 0) { perror("send"); exit(1); }
 
/* receive info */ if ((n = recv(sockfd, buffer, 1024)) < 0) { [7] perror("recv"); exit(1); } buffer[n] = '\0';
 
if ((n=analyze(buffer))== 0) { printf("transaction failure," " try again\n"); } else if (n<0) { printf("login failed, try again\n"); init(); } }
 
}

  1. Create a socket with the socket call.

    AF_INET is the socket type for the Internet communication domain. Note that this parameter must match the protocol and type selected in the corresponding server program.

    Contrast the socket call with the t_open call in the XTI client example ( Section B.1.4). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. Getting information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname routine. [Return to example]

  4. SERVER_PORT is defined in the common.h header file. It is imperative that the same port number be used to connect to the socket server program. The server and client select the port number, which functions as a well known address for communication. [Return to example]

  5. Client issues a connect call to connect to the server. When the connect call is used with a connection-oriented protocol, it allows the client to build a connection with the server before sending data. This is analogous to dialing a phone number. [Return to example]

  6. Send data with the send call. [Return to example]

  7. Receive data with the recv call. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.1.3    XTI Server Program

Example B-3 implements a server using the XTI library for network communication. It is an alternative design for a communication program that makes it transport independent. Compare this program with the socket server program in Section B.1.1. This program has the same limitations described at the beginning of the appendix.

Example B-3: Connection-Oriented XTI Server Program

/*
 *
 *
 * This file contains the main XTI server code
 * for a connection-oriented mode of communication.
 *
 * Usage:       xtiserver
 *
 */
#include "server.h"

 
char *parse(char *); struct transaction *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[]) { int xtifd; int newxtifd; struct sockaddr_in serveraddr; struct hostent *he; int pid; struct t_bind *bindreqp; struct t_bind *bindretp;
 

 
signal(SIGCHLD, SIG_IGN);
 

 
if ((xtifd = t_open("/dev/streams/xtiso/tcp", O_RDWR, [1] NULL)) < 0) { xerror("xti_open", xtifd); exit(1); }
 

 
bzero((char *) &serveraddr, sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; [2] serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); [3] serveraddr.sin_port = htons(SERVER_PORT); [4]
 
/* allocate structures for the t_bind call */ if (((bindreqp=(struct t_bind *) t_alloc(xtifd, T_BIND_STR, T_ALL)) == NULL) || ((bindretp=(struct t_bind *) t_alloc(xtifd, T_BIND_STR, T_ALL)) == NULL)) { xerror("xti_alloc", xtifd); exit(3); }
 

 
bindreqp->addr.buf = (char *)&serveraddr; bindreqp->addr.len = sizeof(serveraddr);
 
/* * Specify how many pending connections can be * maintained, until finish accept processing * */ bindreqp->qlen = 8; [5]
 
if (t_bind(xtifd, bindreqp, (struct t_bind *)NULL) [6] < 0) { xerror("xti_bind", xtifd); exit(4); }
 
/* * Now the socket is ready to accept connections. * For each connection, fork a child process in charge * of the session, and then resume accepting connections. * */
 
while(1) {
 
struct t_call call;
 
if (t_listen(xtifd, &call) < 0) { [7] if (errno == EINTR) { printf("Bye...\n"); exit(0); } else { xerror("t_listen", xtifd); exit(4); } }
 
/* * Create a new transport endpoint on which * to accept a connection * */ if ((newxtifd=t_open("/dev/streams/xtiso/tcp", [8] O_RDWR, NULL)) < 0) { xerror("xti_newopen", xtifd); exit(5); }
 
if (t_bind(newxtifd, [9] (struct t_bind *)NULL, (struct t_bind *)NULL) < 0) { xerror("xti_newbind", xtifd); exit(6);
 
} /* accept connection */ if (t_accept(xtifd, newxtifd, &call) < 0) { [10] xerror("xti_accept", xtifd); exit(7); } pid = fork();
 
switch(pid) { case -1: /* error */ xerror("dosession_fork", xtifd); break; default: t_close(newxtifd); break; case 0: /* child */
 
t_close(xtifd); transactions(newxtifd);
 
t_close(newxtifd); return(0);
 
} }
 
}
 

 
transactions(int fd) [11] { int bytes; char *reply; int dcount; int flags; char datapipe[MAXBUFSIZE+1];
 
/* * Look at the data buffer and parse commands, if more data * required go get it * Since the protocol is SOCK_STREAM oriented, no data * boundaries will be preserved. * */ while (1) { if ((dcount=t_rcv(fd, datapipe, MAXBUFSIZE, [12] &flags)) < 0){ /* if disconnected bid a goodbye */ if (t_errno == TLOOK) { int tmp = t_look(fd);
 
if (tmp != T_DISCONNECT) { t_scope(tmp); } else { exit(0); } } xerror("transactions_receive", fd); break; } if (dcount == 0) { /* consolidate all transactions */ return(0); }
 
datapipe[dcount] = ' '; if ((reply=parse(datapipe)) != NULL) { if (t_snd(fd, reply, strlen(reply), 0) [13] < 0) { xerror("xti_send", fd); break; } } } }

  1. The t_open call specifies a device special file name; for example /dev/streams/xtiso/tcp. This file name provides the necessary abstraction for the TCP transport protocol over IP. Unlike the socket interface, where you specify the address family (for example, AF_INET), this information is already represented in the choice of the device special file. The /dev/streams/xtiso/tcp file implies both TCP transport and IP. See the Chapter 5 for information about STREAMS devices. As mentioned in Section B.1.1, if the OSI transport were available you would use a device such as /dev/streams/xtiso/cots. Contrast the t_open call with the socket call in Section B.1.1. [Return to example]

  2. Selection of the address depends on the choice of the transport protocol. Note that in the socket example the address family was the same as used in the socket system call. With XTI, the choice is not obvious and you must know the appropriate mapping from the transport protocol to sockaddr. See Chapter 3 for more information. [Return to example]

  3. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), ntohs(3). [Return to example]

  4. SERVER_PORT is defined in the common.h header file. It has a data type of short integer which helps identify the server process from other application processes. Numbers from 0 to 1024 are reserved. [Return to example]

  5. Specify the number of pending connections the server can queue while it processes the last request. [Return to example]

  6. Bind the server's address with the t_bind call. The combination of the address and port number uniquely identify it on the network. After the server process' address is bound, the server process is registered on the system and can be identified by the lower level kernel functions to which to direct any requests. [Return to example]

  7. Listen for connection requests with the t_listen function. [Return to example]

  8. Create a new transport endpoint with another call to the t_open function.

    Bind the server's address with the t_bind call. The combination of the address and port number identify it uniquely on the network. [Return to example]

  9. Bind to the new transport endpoint with the t_bind function. [Return to example]

  10. Accept the connection request with the t_accept function. [Return to example]

  11. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided (such as the merchant's login ID, password, and customer's credit card number). This process is repeated until the parse function identifies a complete transaction and returns a response packet, to be sent to the client program.

    The client program can send information packets in any order (and in one or more packets), so the parse function is designed to remember state information sufficient to deal with this unstructured message stream.

    Since the program uses a connection-oriented protocol for data transfer, this function uses t_snd and t_rcv to send and receive data, respectively. [Return to example]

  12. Receive data with the t_rcv function. [Return to example]

  13. Send data with the t_snd function. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.1.4    XTI Client Program

Example B-4 This sample program implements a client program that can communicate with the xtiserver interface shown in Section B.1.3. Compare this program with the socket client program in Example B-3.

Example B-4: Connection-Oriented XTI Client Program

/*
 *
 * This file contains the main XTI client code
 * for a connection-oriented mode of communication.
 *
 * Usage: xticlient [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */

 
#include "client.h"
 
main(int argc, char *argv[]) { int xtifd; struct sockaddr_in serveraddr; struct sockaddr_in clientaddr; struct hostent *he; int n; char *serverhost = "localhost"; struct hostent *serverhostp; char buffer[1024]; char inbuf[1024]; struct t_call sndcall; struct t_call rcvcall; int flags = 0;
 

 
if (argc>1) { serverhost = argv[1]; }
 
init();
 
if ((xtifd = t_open("/dev/streams/xtiso/tcp", O_RDWR, [1] NULL)) < 0) { xerror("xti_open", xtifd); exit(1); }
 

 
bzero((char *) &serveraddr, [2] sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; [3] if ((serverhostp = gethostbyname(serverhost)) == [4] (struct hostent *)NULL) { fprintf(stderr,"gethostbyname on %s failed\n", serverhost); exit(1); } bcopy(serverhostp->h_addr, (char *)&(serveraddr.sin_addr.s_addr), serverhostp->h_length); serveraddr.sin_port = htons(SERVER_PORT); [5]
 
if (t_bind(xtifd, (struct t_bind *)NULL, [6] (struct t_bind *)NULL) < 0) { xerror("bind", xtifd); exit(2); }
 
sndcall.opt.maxlen = 0; sndcall.udata.maxlen = 0; sndcall.addr.buf = (char *)&serveraddr; sndcall.addr.len = sizeof(serveraddr);
 
rcvcall.opt.maxlen = 0; rcvcall.udata.maxlen = 0; rcvcall.addr.buf = (char *)&clientaddr; rcvcall.addr.maxlen = sizeof(clientaddr);
 
if (t_connect(xtifd, &sndcall, [7] (struct t_call *)NULL) < 0) { xerror ("t_connect", xtifd); exit(3); }
 

 
while(1) { /* Merchant record */ sprintf(buffer, "%%%%m%s##%%%%p%s##", merchantname, password);
 
printf("\n\nSwipe card, enter amount: "); fflush(stdout); if (scanf("%s", inbuf) == EOF) { printf("bye...\n"); exit(0); } soundbytes();
 
sprintf(buffer, "%s%%%%a%s##%%%%n%s##", buffer, inbuf, swipecard());
 
if (t_snd(xtifd, buffer, strlen(buffer), 0) [8] < 0) { xerror("t_snd", xtifd); exit(1); }
 
if ((n = t_rcv(xtifd, buffer, 1024, &flags)) [9] < 0) { xerror("t_rcv", xtifd); exit(1); }
 
buffer[n] = '\0';
 
if ((n=analyze(buffer))== 0) { printf("transaction failure," " try again\n"); } else if (n<0) { printf("login failed, try again\n"); init(); } }
 
}

  1. AF_INET is the socket type for the Internet communication domain. If AF_OSI were supported, it could be used to create a socket for OSI communications. The socket type SOCK_STREAM is specified for TCP or connection-oriented communication.

    The t_open call specifies a special device file name instead of the socket address family, socket type, and protocol that the socket call requires. Contrast the socket call in Section B.1.2 with the t_open call. [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. AF_INET specifies the Internet communication domain. If AF_OSI were supported, it could be used to create a socket for OSI communications. [Return to example]

  4. Obtaining information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname routine. [Return to example]

  5. SERVER_PORT is defined in the <common.h> header file. It is imperative that the same port number be used to connect to the XTI server program. Numbers from 0 through 1024 are reserved. [Return to example]

  6. Bind the server address with the t_bind function to enable the client to start sending and receiving data. [Return to example]

  7. Initiate a connection with the server using the t_connect function. [Return to example]

  8. Send data with the t_snd function. [Return to example]

  9. Receive data with the t_rcv function. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.2    Connectionless Programs

This section contains sockets and XTI variations of the same server and client programs, written for connectionless modes of communication.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.2.1    Socket Server Program

Example B-5 implements the server portion of the application in a manner similar to the socket server described in Section B.1.1. Instead of using a connection-oriented paradigm, this program uses a connectionless (datagram/UDP) paradigm for communicating with client programs. This program has the limitations described at the beginning of the appendix.

Example B-5: Connectionless Socket Server Program

/*
 *
 * This file contains the main socket server code
 * for a connectionless mode of communication.
 *
 * Usage:       socketserverDG
 *
 */
#include "server.h"

 
char *parse(char *); struct transaction *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[]) { int sockfd; int newsockfd; struct sockaddr_in serveraddr; int serveraddrlen = sizeof(serveraddr); struct sockaddr_in clientaddr; int clientaddrlen = sizeof(clientaddr); struct hostent *he; int pid;
 

 
signal(SIGCHLD, SIG_IGN);
 

 
/* Create a socket for the communications */ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) [1] < 0) { perror("socket_create"); exit(1); }
 

 
bzero((char *) &serveraddr, [2] sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); [3] serveraddr.sin_port = htons(SERVER_PORT); [4]
 

 
if ( bind(sockfd, [5] (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)) < 0) { perror("socket_bind"); exit(2); }
 
transactions(sockfd);
 
}
 

 
transactions(int fd) [6] { int bytes; char *reply; int dcount; char datapipe[MAXBUFSIZE+1]; struct sockaddr_in serveraddr; int serveraddrlen = sizeof(serveraddr);
 
bzero((char *) &serveraddr, sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(SERVER_PORT);
 
/* * Look at the data buffer and parse commands. * Keep track of the collected data through * transaction_status. * */ while (1) { if ((dcount=recvfrom(fd, datapipe, [7] MAXBUFSIZE, 0, (struct sockaddr *)&serveraddr, &serveraddrlen)) < 0){ perror("transactions_receive"); break; } if (dcount == 0) { return(0); }
 
datapipe[dcount] = '\0';
 
if ((reply=parse(datapipe)) != NULL) { if (sendto(fd, reply, strlen(reply), [8] 0, (struct sockaddr *)&serveraddr, serveraddrlen) < 0) { perror("transactions_sendto"); } } } }

  1. Create a socket with the socket call.

    AF_INET specifies the Internet communication domain. The socket type SOCK_DGRAM is specified for UDP or connectionless communication. This parameter indicates that the program is connectionless.

    Contrast the socket call with the t_open call in the XTI server example ( Section B.2.3). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    The information contained in the sockaddr_in structure is dependent on the address family, which is AF_INET in this example. If AF_OSI were used instead of AF_INET, then sockaddr_osi would be required for the bind call instead of sockaddr_in. [Return to example]

  3. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate
    macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), and ntohs(3). [Return to example]

  4. SERVER_PORT is defined in the <common.h> header file. It has a data type of short integer which helps identify the server process from other application processes. [Return to example]

  5. Bind the server's address to this socket with the bind call. The combination of the address and port number identify it uniquely on the network. After the server process' address is bound, the server process is registered on the system and can be identified by the lower level kernel functions to which to direct requests. [Return to example]

  6. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided (such as the merchant's login ID, password, and customer's credit card number). This process is repeated until the parse function identifies a complete transaction and returns a response packet, to be sent to the client program.

    Since this program uses a connectionless (datagram) protocol, it uses sendto and recvfrom to send and receive data, respectively. [Return to example]

  7. Receive data with the recvfrom call. [Return to example]

  8. Send data with the sendto call. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.2.2    Socket Client Program

Example B-6 implements a socket client that can communicate with the socket server in Example B-5. Section B.2.1. It uses the socket interface in the connectionless, or datagram, mode.

Example B-6: Connectionless Socket Client Program

/*
 *
 * This file contains the main client socket code
 * for a connectionless mode of communication.
 *
 * usage: socketclientDG [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */
#include "client.h"

 
main(int argc, char *argv[]) { int sockfd; struct sockaddr_in serveraddr; int serveraddrlen; struct hostent *he; int n; char *serverhost = "localhost"; struct hostent *serverhostp; char buffer[1024]; char inbuf[1024];
 

 
if (argc>1) { serverhost = argv[1]; }
 
init();
 

 
/* Create a socket for the communications */ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) [1] { perror("socket_create"); exit(1); }
 

 
bzero((char *) &serveraddr, [2] sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET;
 
if ((serverhostp = gethostbyname(serverhost)) == [3] (struct hostent *)NULL) { fprintf(stderr,"gethostbyname on %s failed\n", serverhost); exit(1); } bcopy(serverhostp->h_addr, (char *)&(serveraddr.sin_addr.s_addr), serverhostp->h_length);
 
serveraddr.sin_port = htons(SERVER_PORT); [4]
 
/* Now connect to the server if (connect(sockfd, &serveraddr, [5] sizeof(serveraddr)) < 0) { perror ("connect"); exit(2); } */
 

 
while(1) { /* Merchant record */ sprintf(buffer, "%%%%m%s##%%%%p%s##", merchantname, password);
 
printf("\n\nSwipe card, enter amount: "); fflush(stdout); if (scanf("%s", inbuf) == EOF) { printf("bye...\n"); exit(0); } soundbytes();
 
sprintf(buffer, "%s%%%%a%s##%%%%n%s##", buffer, inbuf, swipecard());
 
if (sendto(sockfd, buffer, strlen(buffer), [6] 0, &serveraddr, sizeof(serveraddr)) < 0) { perror("sendto"); exit(1); }
 
/* receive info */ if ((n = recvfrom(sockfd, buffer, 1024, 0, [7] &serveraddr, &serveraddrlen)) < 0) { perror("recvfrom"); exit(1); } buffer[n] = '\0';
 
if ((n=analyze(buffer))== 0) { printf("transaction failure, " "try again\n"); } else if (n<0) { printf("login failed, try again\n"); init(); } }
 
}

  1. Create a socket with the socket call.

    AF_INET specifies the Internet communication domain. If AF_OSI were supported, it could be used to create a socket for OSI communications. The socket type SOCK_DGRAM is specified for UDP or connectionless communication.

    Contrast the socket call with the t_open call in the XTI client example ( Section B.2.4). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. Getting information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname routine. [Return to example]

  4. SERVER_PORT is defined in the <common.h> header file. It is a short integer, which helps identify the server process from other application processes. [Return to example]

  5. Client issues a connect call to connect to the server. When the connect call is used with a connectionless protocol, it allows the client to store the server's address locally. This means that the client does not have to specify the server's address each time it sends a message. [Return to example]

  6. Send data with the sendto call. [Return to example]

  7. Receive data with the recvfrom call. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.2.3    XTI Server Program

Example B-7 implements a server using the XTI library for network communication. It is an alternative design for a communication program that makes it transport independent. Compare this program with the socket server program in Example B-5. This program has the limitations described at the beginning of the appendix.

Example B-7: Connectionless XTI Server Program

/*
 *
 * This file contains the main XTI server code
 * for a connectionless mode of communication.
 *
 * Usage:       xtiserverDG
 *
 */
#include "server.h"

 
char *parse(char *); struct transaction *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[]) { int xtifd; int newxtifd; struct sockaddr_in serveraddr; struct hostent *he; int pid; struct t_bind *bindreqp; struct t_bind *bindretp;
 

 
signal(SIGCHLD, SIG_IGN);
 

 
/* Create a transport endpoint for the communications */ if ((xtifd = t_open("/dev/streams/xtiso/udp", [1] O_RDWR, NULL)) < 0) { xerror("xti_open", xtifd); exit(1); }
 

 
bzero((char *) &serveraddr, [2] sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; [3] serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); [4] serveraddr.sin_port = htons(SERVER_PORT) [5]
 
/* allocate structures for the t_bind call */ if (((bindreqp=(struct t_bind *)t_alloc(xtifd, T_BIND_STR, T_ALL)) == NULL) || ((bindretp=(struct t_bind *)t_alloc(xtifd, T_BIND_STR, T_ALL)) == NULL)) { xerror("xti_alloc", xtifd); exit(3); }
 

 
bindreqp->addr.buf = (char *)&serveraddr; bindreqp->addr.len = sizeof(serveraddr);
 
/* * Specify how many pending connections can be * maintained, while we finish "accept" processing * */ bindreqp->qlen = 8; [6]
 
if (t_bind(xtifd, bindreqp, (struct t_bind *)NULL) [7] < 0) { xerror("xti_bind", xtifd); exit(4); }
 
/* * Now the server is ready to accept connections * on this socket. For each connection, fork a child * process in charge of the session, and then resume * accepting connections. * */ transactions(xtifd);
 

 
}
 

 
transactions(int fd) [8] { int bytes; char *reply; int dcount; int flags; char datapipe[MAXBUFSIZE+1]; struct t_unitdata unitdata; struct sockaddr_in clientaddr;
 
/* * Look at the data buffer and parse commands. * If more data required, go get it. * */ while (1) { unitdata.udata.maxlen = MAXBUFSIZE; unitdata.udata.buf = datapipe; unitdata.addr.maxlen = sizeof(clientaddr); unitdata.addr.buf = (char *)&clientaddr; unitdata.opt.maxlen = 0;
 
if ((dcount=t_rcvudata(fd, &unitdata, &flags))[9] < 0) { /* if disconnected bid a goodbye */ if (t_errno == TLOOK) { int tmp = t_look(fd);
 
if (tmp != T_DISCONNECT) { t_scope(tmp); } else { exit(0); } } xerror("transactions_receive", fd); break; } if (unitdata.udata.len == 0) {
 
return(0); }
 
datapipe[unitdata.udata.len] = '\0';
 
if ((reply=parse(datapipe)) != NULL) {
 
/* sender's addr is in the unitdata */ unitdata.udata.len = strlen(reply); unitdata.udata.buf = reply;
 
if (t_sndudata(fd, &unitdata) < 0) { [10] xerror("xti_send", fd); break; } } } }

  1. The t_open call specifies a device special file name, which is /dev/streams/xtiso/udp in this example. This file name provides the necessary abstraction for the UDP transport protocol over IP. Unlike the socket interface, where you specify the address family (for example, AF_INET), this information is already represented in the choice of the device special file. The /dev/streams/xtiso/udp file implies both UDP transport and Internet Protocol. See the Chapter 5 for information about STREAMS devices. Contrast the t_open call with the socket call in Section B.2.1. [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain or address family of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an application entity on the network. For TCP/IP and UDP/IP this is the Internet address of the server and the port number on which it is listening.

    The information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. AF_INET specifies the Internet communication domain or address family. [Return to example]

  4. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), and ntohs(3). [Return to example]

  5. SERVER_PORT is defined in the <common.h> header file. It is a short integer, which helps identify the server process from other application processes. Numbers from 0 to 124 are reserved. [Return to example]

  6. Specify the number of pending connections the server can queue while it processes the last request. [Return to example]

  7. Bind the server's address with the t_bind call. The combination of the address and port number identify it uniquely on the network. After the server process' address is bound, the server process is registered on the
    system and can be identified by the lower level kernel functions to which to direct any requests. [Return to example]

  8. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided, such as the merchant's login ID, password, and customer's credit card number. This process is repeated until the parse function identifies a complete transaction and returns a response packet, to be sent to the client program.

    The client program can send information packets in any order (and in one or more packets), so the parse function is designed to remember state information sufficient to deal with this unstructured message stream.

    Since this program uses a connectionless (datagram) protocol, it uses t_sndudata and t_rcvudata to send and receive data, respectively. [Return to example]

  9. Receive data with the t_rcvudata function. [Return to example]

  10. Send data with the t_sndudata function. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.2.4    XTI Client Program

Example B-8 This sample program implements an XTI client that can communicate with the XTI server in Example B-7. It uses the XTI interface in the connectionless, or datagram, mode.

Example B-8: Connectionless XTI Client Program

/*
 *
 * This file contains the main XTI client code
 * for a connectionless mode of communication.
 *
 * usage: client [serverhostname]
 *
 */
#include "client.h"

 
main(int argc, char *argv[]) { int xtifd; struct sockaddr_in serveraddr; struct hostent *he; int n; char *serverhost = "localhost"; struct hostent *serverhostp; char buffer[MAXBUFSIZE+1]; char inbuf[MAXBUFSIZE+1]; struct t_unitdata unitdata; struct t_call sndcall; struct t_call rcvcall; int flags = 0;
 

 
if (argc>1) { serverhost = argv[1]; }
 
init();
 

 
if ((xtifd = t_open("/dev/streams/xtiso/udp", [1] O_RDWR, NULL)) < 0) { xerror("xti_open", xtifd); exit(1); }
 

 
bzero((char *) &serveraddr, [2] sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; [3]
 
if ((serverhostp = gethostbyname(serverhost)) == [4] (struct hostent *)NULL) { fprintf(stderr,"gethostbyname on %s failed\n", serverhost); exit(1); } bcopy(serverhostp->h_addr, (char *)&(serveraddr.sin_addr.s_addr), serverhostp->h_length); /* * SERVER_PORT is a short which identifies * the server process from other sources. * */ serveraddr.sin_port = htons(SERVER_PORT); [5]
 
if (t_bind(xtifd, (struct t_bind *)NULL, [6] (struct t_bind *)NULL) < 0) { xerror("bind", xtifd); exit(2); }
 
while(1) { /* Merchant record */ sprintf(buffer, "%%%%m%s##%%%%p%s##", merchantname, password);
 
printf("\n\nSwipe card, enter amount: "); fflush(stdout);
 
if (scanf("%s", inbuf) == EOF) { printf("bye...\n"); exit(0); }
 
soundbytes();
 
sprintf(buffer, "%s%%%%a%s##%%%%n%s##", buffer, inbuf, swipecard());
 
unitdata.addr.buf = (char *)&serveraddr; unitdata.addr.len = sizeof(serveraddr); unitdata.udata.buf = buffer; unitdata.udata.len = strlen(buffer); unitdata.opt.len = 0;
 
if (t_sndudata(xtifd, &unitdata) < 0) { [7] xerror("t_snd", xtifd); exit(1); }
 
unitdata.udata.maxlen = MAXBUFSIZE; unitdata.addr.maxlen = sizeof(serveraddr);
 
/* receive info */ if ((t_rcvudata(xtifd, &unitdata, &flags)) [8] < 0) { xerror("t_rcv", xtifd); exit(1); }
 
buffer[unitdata.udata.len] = '\0';
 
if ((n=analyze(buffer))== 0) { printf("transaction failure, " "try again\n"); } else if (n<0) { printf("login failed, try again\n"); init(); } }
 
}

  1. The t_open call specifies a device special file name; for example /dev/streams/xtiso/udp. This file name provides the necessary abstraction for the UDP transport protocol over IP. Unlike the socket interface, where you specify the address family (for example, AF_INET), this information is already represented in the choice of the device special file. The /dev/streams/xtiso/udp file implies both UDP transport and Internet Protocol. See the Chapter 5 for information about STREAMS devices. Contrast the t_open call with the socket call in Section B.2.2. [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    The information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. AF_INET specifies the Internet communication domain. If AF_OSI were supported it could be used to create a socket for OSI communications. [Return to example]

  4. Getting information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname(3) routine. [Return to example]

  5. SERVER_PORT is defined in the <common.h> header file. It is a short integer, which helps identify the server process from other application processes. [Return to example]

  6. Bind the server address with the t_bind function to enable the client to start sending and receiving data. [Return to example]

  7. Send data with the t_sndudata function. [Return to example]

  8. Receive data with the t_rcvudata function. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3    Common Code

The following header and database files are required for all or several of the client and server portions of this application:


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3.1    The common.h Header File

Example B-9 shows the <common.h> header file. It contains common header files and constants required by all sample programs.

Example B-9: The common.h Header File

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>             [1]
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <fcntl.h>
#include <xti.h>

 

 
#define SEPARATOR ',' #define PREAMBLE "%%" #define PREAMBLELEN 2 [2] #define POSTAMBLE "##" #define POSTAMBLELEN 2
 

 
/* How to contact the server */ #define SERVER_PORT 1234 [3] /* How to contact the client (for datagram only) */ #define CLIENT_PORT 1235
 
#define MAXBUFSIZE 4096

  1. List of header files to include. [Return to example]

  2. These statements define constants that allow more effective parsing of data exchanged between the server and client. [Return to example]

  3. SERVER_PORT is a well known port that is arbitrarily assigned by the programmer so that clients can communicate with the server. SERVER_PORT is used to identify the service to which you want to connect. Port numbers 0 through 1024 are reserved for the system. Programmers can choose a number, as long as it does not conflict with any other applications. While debugging, this number is chosen randomly (and by trial and error). For a well-distributed application, some policy must be used to avoid conflicts with other applications. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3.2    The server.h Header File

Example B-10 shows the <server.h> header file. It contains the data structures for accessing the server's database, as well as the data structures for analyzing and synthesizing messages to and from clients.

Example B-10: The server.h Header File

#include "common.h"

 
struct merchant { char *name; char *passwd; };
 
struct customer { char *cardnum; char *name; int limit; int balance; struct transaction *tlist; /* presumably other data */ };
 
struct transaction { struct transaction *nextcust; struct transaction *nextglob; struct customer *whose; char *merchantname; int amount; char *verification; };
 
extern struct transaction *alltransactions; extern struct merchant merchant[]; extern int merchantcount; extern struct customer customer[]; extern int customercount;
 
#define INVALID (struct transaction *)1
 
#define MERCHANTAUTHERROR "%%A##" #define USERAUTHERROR "%%U##" #define USERAMOUNTERROR "%%V##" #define TRANSMITERROR "deadbeef"
 
/* define transaction_status flags */ #define NAME 0x01 #define PASS 0x02 #define AMOUNT 0x04 #define NUMBER 0x08
 
#define AUTHMASK 0x03 #define VERIMASK 0x0C


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3.3    The serverauth.c File

Example B-11 shows the serverauth.c file.

Example B-11: The serverauth.c File

/*
 *
 * Authorization information (not related to the
 * networking interface)
 *
 */

 
#include "server.h"
 

 
/* * Currently a simple non-encrypted password method to search db * */ authorizemerchant(char *merch, char *password) { struct merchant *mp;
 
for(mp = merchant; (mp)->name != (char *)NULL; mp++) { if (!strcmp(merch, (mp)->name)) { return (!strcmp(password, (mp)->passwd)); } } return(0); }
 

 
struct transaction * verifycustomer(char *num, int amount, char *merchant) { char buf[64]; struct customer *cp; struct transaction *tp;
 
for(cp = customer; (cp)->cardnum != NULL; cp++) { if (!strcmp(num, (cp)->cardnum)) { if (amount <= (cp)->balance) { (cp)->balance -= amount; if ((tp = malloc(sizeof( struct transaction))) == NULL) { printf("Malloc error\n"); return(NULL); } tp->merchantname = merchant; tp->amount = amount; sprintf(buf, "v%012d", time(0)); if ((tp->verification = malloc(strlen(buf)+1)) == NULL) { printf("Malloc err\n"); return(NULL); } strcpy(tp->verification, buf); tp->nextcust = cp->tlist; tp->whose = cp; cp->tlist = tp; tp->nextglob = alltransactions; alltransactions = tp; return(tp); } else { return(NULL); } } } return(INVALID);
 
}
 
int transaction_status; int authorized = 0; int amount = 0; char number[256]; char Merchant[256]; char password[256];
 

 
char * parse(char *cp) { char *dp, *ep; unsigned char type; int doauth = 0; char *buffer;
 
dp = cp; if ((buffer=malloc(256)) == NULL) { return(TRANSMITERROR); }
 
while (*dp) { /* terminate the string at the postamble */ if (!(ep=strstr(dp, POSTAMBLE))) { return(TRANSMITERROR); } *ep = '\0'; ep = ep + POSTAMBLELEN; [1]
 
/* search for preamble */ if (!(dp=strstr(dp, PREAMBLE))) { return(TRANSMITERROR); } dp += PREAMBLELEN;
 
/* Now get the token */ type = *dp++;
 
switch(type) { case 'm': strcpy(Merchant, dp); transaction_status |= NAME; break; case 'p': strcpy(password, dp); transaction_status |= PASS; break; case 'n': transaction_status |= NUMBER; strcpy(number, dp); break; case 'a': transaction_status |= AMOUNT; amount = atoi(dp); break; default: printf("Bad command\n"); return(TRANSMITERROR); } if ((transaction_status & AUTHMASK) == AUTHMASK) { transaction_status &= ~AUTHMASK; authorized = authorizemerchant( Merchant, password); if (!authorized) { printf("Merchant not" " authorized\n"); return(MERCHANTAUTHERROR); } }
 
/* * If both amount and number gathered, * do verification * */ if ((authorized) && ((transaction_status&VERIMASK) ==VERIMASK)) { struct transaction *tp;
 
transaction_status &= ~VERIMASK; /* send a verification back */ if ((tp=verifycustomer(number, amount, Merchant)) == NULL) { return(USERAMOUNTERROR); } else if (tp==INVALID) { return(USERAUTHERROR); } else { sprintf(buffer, "%%%%%s##%%%%c%s##%%%%m%s##", tp->verification, tp->whose->name, tp->merchantname); return(buffer); } }
 
dp = ep; } return(NULL); }

  1. This function parses the incoming data, which includes the merchant authorization information, customer's credit card number, and the amount the customer is charging. Note that the function can not assume that all of the information is available in one message because the underlying TCP protocol is stream-oriented. This function can be simplified if a datagram type service is used or if a protocol that uses sequenced packets (SEQPACKET) is used. The function is designed to accept pieces of information in any order and in one or more message blocks. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3.4    The serverdb.c File

Example B-12 shows the serverdb.c file.

Example B-12: The serverdb.c File

/*
 *
 * Database of valid merchants and credit card customers with the
 * credit limits, etc.
 *
 *
 */
#include "server.h"

 
struct merchant merchant[] = { {"abc", "abc"}, {"magic", "magic"}, {"gasco", "gasco"}, {"furnitureco", "abc"}, {"groceryco", "groceryco"}, {"bakeryco", "bakeryco"}, {"restaurantco", "restaurantco"}, {NULL, NULL} };
 
int merchantcount = sizeof(merchant)/sizeof(struct merchant)-1;
 
struct customer customer[] = { { "4322546789701000", "John Smith", 1000, 800 }, { "4322546789701001", "Bill Stone", 2000, 200 }, { "4322546789701002", "Dave Adams", 1500, 500 }, { "4322546789701003", "Ray Jones", 1200, 800 }, { "4322546789701004", "Tony Zachry", 1000, 100 }, { "4322546789701005", "Danny Einstein", 5000, 50 }, { "4322546789701006", "Steve Simonyi", 10000, 5800}, { "4322546789701007", "Mary Ming", 1100, 700 }, { "4322546789701008", "Joan Walters", 800, 780 }, { "4322546789701009", "Gail Newton", 1000, 900 }, { "4322546789701010", "Jon Robertson", 1000, 1000}, { "4322546789701011", "Ellen Bloop", 1300, 800 }, { "4322546789701012", "Sue Svelter", 1400, 347 }, { "4322546789701013", "Suzette Ring", 1200, 657 }, { "4322546789701014", "Daniel Mattis", 1600, 239 }, { "4322546789701015", "Robert Esconis", 1800, 768 }, { "4322546789701016", "Lisa Stiles", 1100, 974 }, { "4322546789701017", "Bill Brophy", 1050, 800 }, { "4322546789701018", "Linda Smitten", 4000, 200 }, { "4322546789701019", "John Norton", 1400, 900 }, { "4322546789701020", "Danielle Smith", 2000, 640 }, { "4322546789701021", "Amy Olds", 1300, 100 }, { "4322546789701022", "Steve Smith", 2000, 832 }, { "4322546789701023", "Robert Smart", 3000, 879 }, { "4322546789701024", "Jon Harris", 500, 146 }, { "4322546789701025", "Adam Gershner", 1600, 111 }, { "4322546789701026", "Mary Papadimis", 2000, 382 }, { "4322546789701027", "Linda Jones", 1300, 578 }, { "4322546789701028", "Lucy Barret", 1400, 865 }, { "4322546789701029", "Marie Gilligan", 1000, 904 }, { "4322546789701030", "Kim Coyne", 3000, 403 }, { "4322546789701031", "Mike Storm", 7500, 5183}, { "4322546789701032", "Cliff Clayden", 750, 430 }, { "4322546789701033", "John Turing", 4000, 800 }, { "4322546789701034", "Jane Joyce", 10000, 8765}, { "4322546789701035", "Jim Roberts", 4000, 3247}, { "4322546789701036", "Stevw Stephano", 1750, 894 }, {NULL, NULL} };
 
struct transaction * alltransactions = NULL; int customercount = sizeof(customer)/sizeof(struct customer)-1;


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3.5    The xtierror.c File

Example B-13 shows the xtierror.c file. It is used to generate a descriptive message in case of an error. Note that for asynchronous errors or events, the t_look function is used to get more information.

Example B-13: The xtierror.c File

#include <xti.h>
#include <stdio.h>

 
int xerror(char *marker, int fd) { fprintf(stderr, "%s error [%d]\n", marker, t_errno); t_error("Transport Error"); if (t_errno == TLOOK) { t_scope(t_look(fd)); }
 
}
 
int t_scope(int tlook) { char *tmperr;
 
switch(tlook) { case T_LISTEN: tmperr = "connection indication"; break; case T_CONNECT: tmperr = "connect confirmation"; break; case T_DATA: tmperr = "normal data received"; break; case T_EXDATA: tmperr = "expedited data"; break; case T_DISCONNECT: tmperr = "disconnect received"; break; case T_UDERR: tmperr = "datagram error"; break; case T_ORDREL: tmperr = "orderly release indication"; break; case T_GODATA: tmperr = "flow control restriction lifted"; break; case T_GOEXDATA: tmperr = "flow control restriction " "on expedited data lifted"; break; default: tmperr = "unknown event"; } fprintf(stderr, "Asynchronous event: %s\n", tmperr); }


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3.6    The client.h Header File

Example B-14 shows the client.h header file.

Example B-14: The client.h File

#include "common.h"

 
extern char merchantname[]; extern char password[];


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


B.3.7    The clientauth.c File

Example B-15 shows the clientauth.c file. It contains the code that obtains the merchant's authorization, as well as the logic to analyze the message sent from the server. The resulting message is interpreted to see if the authorization was granted or rejected by the server.

Example B-15: The clientauth.c File

#include "client.h"

 
init() { printf("\nlogin: "); fflush(stdout); scanf("%s", merchantname);
 
printf("Password: "); fflush(stdout); scanf("%s", password);
 
srandom(time(0)); }
 
/* simulate some network activity via sound */ soundbytes() { int i;
 
for(i=0;i<11;i++) { printf(); fflush(stdout); usleep(27000*(random()%10+1)); } }
 
analyze(char *cp) {
 
char *dp, *ep; unsigned char type; char customer[128]; char verification[128];
 
customer[0] = verification[0] = '\0';
 
dp = cp;
 
while ((dp!=NULL) && (*dp)) { /* terminate the string at the postamble */ if (!(ep=strstr(dp, POSTAMBLE))) { return(0); } *ep = '\0'; ep = ep + POSTAMBLELEN;
 
/* search for preamble */ if (!(dp=strstr(dp, PREAMBLE))) { return(0); } dp += PREAMBLELEN;
 
/* Now get the token */ type = *dp++;
 
switch(type) { case 'm': if (strcmp(merchantname, dp)) { return(0); } break; case 'c': strcpy(customer, dp); break; case 'U': printf("Authorization denied\n"); return(1); case 'V': printf("Amount exceeded\n"); return(1); case 'A': return(-1); case 'v': strcpy(verification, dp); break; default: return(0); } dp = ep; } if (*customer && *verification) { printf("%s, verification ID: %s\n", customer, verification); return(1); } return(0); }


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


B.3.8    The clientdb.c File

Example B-16 shows the clientdb.c file. It contains a database of customer credit card numbers used to simulate the card swapping action. In a real world application, a magnetic reader reads the numbers through an appropriate interface. Also, the number cache is not required for a real world application.

Example B-16: The clientdb.c File

/*
 *
 * Database of customer credit card numbers to simulate
 * the card swapping action. In practice the numbers
 * will be read by magnetic readers through an
 * appropriate interface.
 */

 
#include <time.h>
 
char merchantname[256]; char password[256];
 

 
char *numbercache[] = { "4322546789701000", "4322546789701001", "4322546789701002", "4222546789701002", /* fake id */ "4322546789701003", "4322546789701004", "4322546789701005", "4322546789701006", "4322546789701007", "4322546789701008", "4322546789701009", "4322546789701010", "4322546789701011", "4322546789701012", "4322546789701013", "4322546789701014", "4322546789701015", "4322546789701016", "4322546789701017", "4322546789701018", "4222546789701018", /* fake id */ "4322546789701019", "4322546789701020", "4322546789701021", "4322546789701022", "4322546789701023", "4322546789701024", "4322546789701025", "2322546789701025", /* fake id */ "4322546789701026", "4322546789701027", "4322546789701028", "4322546789701029", "4322546789701030", "4322546789701031", "4322546789701032", "4322546789701033", "4322546789701034", "4322546789701035", "4322546789701036", };
 
#define CACHEENTRIES (sizeof(numbercache)/sizeof(char *))
 

 
char * swipecard() { return(numbercache[random()%CACHEENTRIES]); }