This appendix contains annotated files for a sample server/client[Footnote 3] credit card authorization program. Clients access a server on the merchant's behalf and request authorization from the server to put a charge on the client's credit card. The server maintains a database of authorized merchants and their passwords, as well as a database of credit card customers, their credit limit, and current balance. It either authorizes or rejects a client request based on the information in its database.
Several variations on the credit card authorization program are presented, including connection-oriented and connectionless modes. The connection-oriented and connectionless modes each contain socket and XTI code for the server and client portions of the program.
Although the program uses network programming in a real world application, it has the following limitations:
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] } } }
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]
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]
htonl
(3),
htons
(3),
ntohl
(3),
and
ntohs
(3).
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]
bind
call. The combination of the address and port number
identify it uniquely on the network.
[Return to example]
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]
parse
function, which tracks the information provided, such as the
merchant's login ID, password, and customer's credit card number.
This process is
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]
recv
call.
[Return to example]
send
call.
[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(); } }
}
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]
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]
gethostbyname
routine.
[Return to example]
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]
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]
send
call.
[Return to example]
recv
call.
[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; } } } }
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]
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]
htonl
(3),
htons
(3),
ntohl
(3),
ntohs
(3).
[Return to example]
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]
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]
t_listen
function.
[Return to example]
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]
t_bind
function.
[Return to example]
t_accept
function.
[Return to example]
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]
t_rcv
function.
[Return to example]
t_snd
function.
[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]
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]
gethostbyname
routine.
[Return to example]
<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]
t_bind
function to enable the client to start sending and receiving data.
[Return to example]
t_connect
function.
[Return to example]
t_snd
function.
[Return to example]
t_rcv
function.
[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"); } } } }
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]
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]
htonl
(3),
htons
(3),
ntohl
(3),
and
ntohs
(3).
[Return to example]
<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]
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]
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]
recvfrom
call.
[Return to example]
sendto
call.
[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(); } }
}
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]
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]
gethostbyname
routine.
[Return to example]
<common.h>
header file. It is a short integer, which helps identify the server process
from other application processes.
[Return to example]
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]
sendto
call.
[Return to example]
recvfrom
call.
[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; } } } }
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]
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]
htonl
(3),
htons
(3),
ntohl
(3),
and
ntohs
(3).
[Return to example]
<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]
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
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]
t_rcvudata
function.
[Return to example]
t_sndudata
function.
[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(); } }
}
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]
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]
gethostbyname
(3)
routine.
[Return to example]
<common.h>
header file. It is a short integer, which helps identify the server process
from other application processes.
[Return to example]
t_bind
function to enable the client to start sending and receiving data.
[Return to example]
t_sndudata
function.
[Return to example]
t_rcvudata
function.
[Return to example]
The following header and database files are required for all or several of the client and server portions of this application:
<common.h>
<server.h>
serverauth.c
serverdb.c
xtierror.c
<client.h>
clientauth.c
clientdb.c
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]); }