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.
This section contains sockets and XTI variations of the same server and client programs, written for connection-oriented modes communication.
Example B-1 implements a server using the socket interface.
/* * * 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] } } }
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]
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]
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]
Example B-2 implements a client program that can communicate with the socketserver interface shown in Example B-1.
/* * * 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(); } }
}
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]
Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]
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.
/* * * * 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; } } } }
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]
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]
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.
/* * * 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(); } }
}
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]
Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]
This section contains sockets and XTI variations of the same server and client programs, written for connectionless modes of communication.
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.
/* * * 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"); } } } }
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]
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]
Since this program uses a connectionless (datagram) protocol, it uses sendto and recvfrom to send and receive data, respectively. [Return to example]
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.
/* * * 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(); } }
}
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]
Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]
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.
/* * * 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; } } } }
The information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]
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]
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.
/* * * 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(); } }
}
The information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]
The following header and database files are required for all or several of the client and server portions of this application:
Example B-9 shows the <common.h> header file. It contains common header files and constants required by all sample programs.
#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
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.
#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
Example B-11 shows 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); }
Example B-12 shows 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;
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.
#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); }
Example B-14 shows the client.h header file.
#include "common.h"
extern char merchantname[]; extern char password[];
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.
#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); }
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.
/* * * 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]); }