ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/host/c++/tcp_util.c
Revision: 1.2
Committed: Mon Feb 26 14:42:43 2001 UTC (23 years, 6 months ago) by ab11
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +0 -0 lines
State: FILE REMOVED
Log Message:
DELETED

File Contents

# User Rev Content
1 ab11 1.1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2     <!-- saved from url=(0068)http://www.interlog.com/~calex/software/source/lib/libgpl/tcp_util.c -->
3     <HTML><HEAD>
4     <META content="text/html; charset=windows-1252" http-equiv=Content-Type>
5     <META content="MSHTML 5.00.3105.105" name=GENERATOR></HEAD>
6     <BODY><XMP>/************************************************************************
7     * Copyright (c) 1996 by Charles A. Measday *
8     * *
9     * Permission to use, copy, modify, and distribute this software *
10     * and its documentation for any purpose and without fee is hereby *
11     * granted, provided that the above copyright notice appear in all *
12     * copies. The author makes no representations about the suitability *
13     * of this software for any purpose. It is provided "as is" without *
14     * express or implied warranty. *
15     ************************************************************************/
16    
17     /*
18     @(#) FILE: tcp_util.c RELEASE: 1.16 DATE: 02/18/98, 17:51:23
19     */
20     /*******************************************************************************
21    
22     File:
23    
24     tcp_util.c
25    
26     TCP/IP Networking Utilities.
27    
28    
29     Author: Alex Measday, ISI
30    
31    
32     Purpose:
33    
34     The TCP_UTIL functions allow you to easily establish and communicate
35     over TCP/IP network connections between client and server processes,
36     possibly residing on different hosts. The TCP_UTIL functions follow
37     a telephone-like model of networking: clients "call" servers and
38     servers "answer" clients. Once a network connection is established
39     between a client and a server, the two can "talk" to each other by
40     reading from and writing to the connection:
41    
42     Client <----------------------> Server
43    
44     tcpListen(), tcpAnswer(), and tcpCall() are used to establish a
45     communications link between a client and a server. The server process
46     calls tcpListen() to create a network endpoint at which the server
47     listens for connection requests from clients. When one is received,
48     the server process calls tcpAnswer() to accept the client connection.
49     A server may receive and accept connection requests from multiple
50     clients; the operating system automatically creates a new connection
51     (data endpoint) for each client. The server can then multiplex the
52     servicing of the clients or, if the operating system supports it,
53     fork separate subprocesses to service each client.
54    
55     Client processes call tcpCall() to submit connection requests to
56     the server process (which may be running on another host). Note
57     that a process can be both a client and a server with respect to
58     other processes.
59    
60     tcpRead() and tcpWrite() are used to send and receive data over a
61     network connection. Because there is no concept of record boundaries
62     in a TCP/IP network stream, communicating processes must follow their
63     own protocol in order to determine how long a message is. In the
64     example presented below, the protocol is very simple: every message
65     is exactly 64 bytes long, even if the text of the message doesn't
66     fill the transmitted "record". More sophisticated protocols (e.g.,
67     the XDR record marking standard) are available.
68    
69     The following is a very simple server process that listens for and
70     answers a client "call". It then reads and displays 64-byte messages
71     sent by the client. If the client "hangs up", the server loops back
72     and waits for another client.
73    
74     #include <stdio.h> -- Standard I/O definitions.
75     #include "tcp_util.h" -- TCP/IP networking utilities.
76    
77     main (int argc, char *argv[])
78     {
79     char buffer[128] ;
80     TcpEndpoint client, server ;
81    
82     tcpListen ("<name>", 99, &server) ; -- Create listening endpoint.
83    
84     for ( ; ; ) { -- Answer next client.
85     tcpAnswer (server, -1.0, &client) ;
86     for ( ; ; ) { -- Service connected client.
87     if (tcpRead (client, -1.0, 64, buffer, NULL)) break ;
88     printf ("Message from client: %s\n", buffer) ;
89     }
90     tcpDestroy (client) ; -- Lost client.
91     }
92    
93     }
94    
95     The following client process "calls" the server and, once the connection
96     is established, sends it 16 messages:
97    
98     #include <stdio.h> -- Standard I/O definitions.
99     #include "tcp_util.h" -- TCP/IP networking utilities.
100    
101     main (int argc, char *argv[])
102     {
103     char buffer[128] ;
104     int i ;
105     TcpEndpoint server ;
106    
107     tcpCall ("<name>", 0, &server) ; -- Call server.
108    
109     for (i = 0 ; i < 16 ; i++) { -- Send messages.
110     sprintf (buffer, "Hello for the %dth time!", i) ;
111     tcpWrite (server, -1.0, 64, buffer, NULL) ;
112     }
113    
114     tcpDestroy (server) ; -- Hang up.
115    
116     }
117    
118    
119     Notes:
120    
121     These functions are reentrant under VxWorks (except for the global
122     debug flag).
123    
124    
125     Ancient History (C to C++ and back again):
126    
127     This package borrowed 95% of its code and comments from a C++ version
128     of my NET_UTIL package, so I hope the comments are all up to date.
129    
130     I'm not crazy about the term "endpoint", but I wanted something other
131     than "socket" that was common to TCP listening sockets and TCP and
132     UDP data sockets. I tried "port", but TCP server sockets all have
133     their listening socket's port number. I've seen "endpoint" used in
134     a couple of networking books and in some articles, so there it is.
135    
136    
137     Public Procedures (for listening endpoints):
138    
139     tcpAnswer() - answers a client connection request.
140     tcpListen() - creates a listening endpoint.
141     tcpRequestPending() - checks if a client is trying to connect.
142    
143     Public Procedures (for data endpoints, * defined as macros):
144    
145     tcpCall() - establishes a client-side connection with a server.
146     tcpComplete() - completes a no-wait call.
147     tcpIsReadable() - checks if data is waiting to be read on a connection.
148     tcpIsUp() - checks if a connection is up.
149     tcpIsWriteable() - checks if data can be written to a connection.
150     tcpRead() - reads data from a connection.
151     * tcpSetBuf() - changes the sizes of a connection's receive and send buffers.
152     tcpWrite() - writes data to a connection.
153    
154     Public Procedures (general):
155    
156     tcpDestroy() - closes an endpoint.
157     tcpFd() - returns the file descriptor for an endpoint's socket.
158     tcpName() - returns the name of an endpoint.
159    
160     *******************************************************************************/
161    
162     #define _BSD_SOURCE 1
163    
164     #include <ctype.h> /* Standard character functions. */
165     #include <errno.h> /* System error definitions. */
166     #include <limits.h> /* Maximum/minimum value definitions. */
167     #ifndef PATH_MAX
168     # include <sys/param.h> /* System parameters. */
169     # define PATH_MAX MAXPATHLEN
170     #endif
171     #include <stdio.h> /* Standard I/O definitions. */
172     #include <stdlib.h> /* Standard C Library definitions. */
173     #include <string.h> /* C Library string functions. */
174    
175     #if defined(VMS)
176     # include <socket.h> /* Socket-related definitions. */
177     # include <ucx$inetdef.h> /* VMS/Ultrix Connection definitions. */
178     # include <unixio.h> /* VMS-emulation of UNIX I/O. */
179     # include "fd.h" /* File descriptor set definitions. */
180     # define MAXHOSTNAMELEN 64
181     #elif defined(VXWORKS)
182     # include <hostLib.h> /* Host library definitions. */
183     # include <ioLib.h> /* I/O library definitions. */
184     # include <selectLib.h> /* SELECT(2) definitions. */
185     # include <socket.h> /* Socket-related definitions. */
186     # include <sockLib.h> /* Socket library definitions. */
187     # include <sysLib.h> /* System library definitions. */
188     # include <taskLib.h> /* Task library definitions. */
189     # define sleep(seconds) taskDelay ((seconds) * sysClkRateGet())
190     # include <types.h> /* System type definitions. */
191     # include <unistd.h> /* UNIX I/O definitions. */
192     # include <sys/ioctl.h> /* I/O control definitions. */
193     # include <sys/times.h> /* System time definitions. */
194     #else
195     # include <netdb.h> /* Network database definitions. */
196     # include <unistd.h> /* UNIX I/O definitions. */
197     # include <sys/ioctl.h> /* I/O control definitions. */
198     # include <sys/param.h> /* System parameters. */
199     # include <sys/socket.h> /* Socket-related definitions. */
200     # include <sys/time.h> /* System time definitions. */
201     # include <sys/types.h> /* System type definitions. */
202     #endif
203    
204     #include <netinet/in.h> /* Internet IPC domain definitions. */
205    
206     #include "meo_util.h" /* Memory operations. */
207     #include "net_util.h" /* Networking utilities. */
208     #include "skt_util.h" /* Socket support functions. */
209     #include "str_util.h" /* String manipulation functions. */
210     #include "tv_util.h" /* "timeval" manipulation functions. */
211     #include "vperror.h" /* VPERROR() definitions. */
212     #include "tcp_util.h" /* TCP/IP networking utilities. */
213    
214     /* Delete an endpoint without
215     overwriting the value of ERRNO. */
216     #define CLEAN_UP(endpoint) \
217     { int status = errno ; \
218     tcpDestroy (endpoint) ; \
219     endpoint = NULL ; \
220     errno = status ; \
221     }
222    
223    
224     /*******************************************************************************
225     TCP Endpoint - contains information about a server's listening endpoint
226     or about a client's or server's data endpoint.
227     *******************************************************************************/
228    
229     typedef enum TcpEndpointType {
230     TcpNone, TcpListeningPoint, TcpDataPoint
231     } TcpEndpointType ;
232    
233     typedef struct _TcpEndpoint {
234     char *name ; /* "<port>[@<host>]" */
235     TcpEndpointType type ; /* Listening or data endpoint? */
236     int fd ; /* Listening or data socket. */
237     } _TcpEndpoint ;
238    
239    
240     int tcp_util_debug = 0 ; /* Global debug switch (1/0 = yes/no). */
241    
242     /*******************************************************************************
243    
244     Procedure:
245    
246     tcpAnswer ()
247    
248     Answer a Client's Connection Request.
249    
250    
251     Purpose:
252    
253     The tcpAnswer() function waits for and answers a client's request for a
254     network connection. When a request is accepted, the system automatically
255     creates a new socket (the "data" socket) through which the server can talk
256     to the client.
257    
258    
259     Invocation:
260    
261     status = tcpAnswer (listeningPoint, timeout, &dataPoint) ;
262    
263     where
264    
265     <listeningPoint> - I
266     is the listening endpoint handle returned by tcpListen().
267     <timeout> - I
268     specifies the maximum amount of time (in seconds) that the
269     caller wishes to wait for a connection to be established.
270     A fractional time can be specified; e.g., 2.5 seconds.
271     A negative timeout (e.g., -1.0) causes an infinite wait;
272     a zero timeout (0.0) causes an immediate return if no
273     connection request is pending.
274     <dataPoint> - O
275     returns a handle for the data endpoint created by the acceptance
276     of a connection request from another net location (i.e., a client).
277     <status> - O
278     returns the status of answering a network connection request:
279     zero if there were no errors, EWOULDBLOCK if the timeout interval
280     expired before a connection was established, and ERRNO otherwise.
281    
282     *******************************************************************************/
283    
284    
285     int tcpAnswer (
286    
287     # if __STDC__
288     TcpEndpoint listeningPoint,
289     double timeout,
290     TcpEndpoint *dataPoint)
291     # else
292     listeningPoint, timeout, dataPoint)
293    
294     TcpEndpoint listeningPoint ;
295     double timeout ;
296     TcpEndpoint *dataPoint ;
297     # endif
298    
299     { /* Local variables. */
300     char *hostName, serverName[PATH_MAX+1] ;
301     fd_set readMask ;
302     int length, numActive, optval ;
303     struct sockaddr clientAddress ;
304     struct timeval deltaTime, expirationTime ;
305    
306    
307    
308    
309    
310     /*******************************************************************************
311     Wait for the next connection request from a client.
312     *******************************************************************************/
313    
314     /* If a timeout interval was specified, then compute the expiration time
315     of the interval as the current time plus the interval. */
316    
317     if (timeout >= 0.0)
318     expirationTime = tvAdd (tvTOD (), tvCreateF (timeout)) ;
319    
320     /* Wait for a connection request from a client. */
321    
322     for ( ; ; ) {
323     if (timeout >= 0.0)
324     deltaTime = tvSubtract (expirationTime, tvTOD ()) ;
325     FD_ZERO (&readMask) ; FD_SET (listeningPoint->fd, &readMask) ;
326     numActive = select (listeningPoint->fd+1, &readMask, NULL, NULL,
327     (timeout < 0.0) ? NULL : &deltaTime) ;
328     if (numActive >= 0) break ;
329     if (errno == EINTR) continue ; /* Retry on signal interrupt. */
330     vperror ("(tcpAnswer) Error waiting for connection request on %s.\nselect: ",
331     listeningPoint->name) ;
332     return (errno) ;
333     }
334    
335     if (numActive == 0) {
336     errno = EWOULDBLOCK ;
337     vperror ("(tcpAnswer) Timeout while waiting for connection request on %s.\n",
338     listeningPoint->name) ;
339     return (errno) ;
340     }
341    
342    
343     /*******************************************************************************
344     Answer the connection request.
345     *******************************************************************************/
346    
347     /* Create an endpoint structure for the pending connection request. */
348    
349     *dataPoint = (TcpEndpoint) malloc (sizeof (_TcpEndpoint)) ;
350     if (*dataPoint == NULL) {
351     vperror ("(tcpAnswer) Error allocating endpoint structure for %s.\nmalloc: ",
352     listeningPoint->name) ;
353     return (errno) ;
354     }
355    
356     (*dataPoint)->name = NULL ;
357     (*dataPoint)->type = TcpDataPoint ;
358     (*dataPoint)->fd = -1 ;
359    
360     /* Accept the connection request. */
361    
362     do { /* Retry interrupted system calls. */
363     length = sizeof clientAddress ;
364     (*dataPoint)->fd = accept (listeningPoint->fd,
365     &clientAddress, &length) ;
366     } while (((*dataPoint)->fd < 0) && (errno == EINTR)) ;
367    
368     if ((*dataPoint)->fd < 0) {
369     if (errno != EWOULDBLOCK)
370     vperror ("(tcpAnswer) Error accepting connection request on %s.\naccept: ",
371     listeningPoint->name) ;
372     CLEAN_UP (*dataPoint) ;
373     return (errno) ;
374     }
375    
376     /* Configure the socket so that the operating system periodically verifies
377     that the connection is still alive by "pinging" the client. */
378    
379     optval = 1 ; /* Enable keep-alive transmissions. */
380     if (setsockopt ((*dataPoint)->fd, SOL_SOCKET, SO_KEEPALIVE,
381     (char *) &optval, sizeof optval) == -1) {
382     vperror ("(tcpAnswer) Error enabling keep-alive mode for %s's client connection.\nsetsocketopt: ",
383     listeningPoint->name) ;
384     CLEAN_UP (*dataPoint) ;
385     return (errno) ;
386     }
387    
388     /* Construct the connection's name. */
389    
390     hostName = (char *) sktPeer (listeningPoint->name, (*dataPoint)->fd) ;
391     if (hostName == NULL) hostName = "localhost" ;
392     sprintf (serverName, "%d",
393     sktPort (listeningPoint->name, (*dataPoint)->fd)) ;
394     (*dataPoint)->name = malloc (strlen (serverName) + 1 +
395     strlen (hostName) + 1) ;
396     if ((*dataPoint)->name == NULL) {
397     vperror ("(tcpAnswer) Error duplicating server name: %s#%s\nmalloc: ",
398     serverName, hostName) ;
399     CLEAN_UP (*dataPoint) ;
400     return (errno) ;
401     }
402     sprintf ((*dataPoint)->name, "%s#%s", serverName, hostName) ;
403    
404    
405     /* Return the data connection to the caller. */
406    
407     if (tcp_util_debug) printf ("(tcpAnswer) Accepted connection %s, socket %d.\n",
408     (*dataPoint)->name, (*dataPoint)->fd) ;
409    
410     return (0) ;
411    
412     }
413    
414     /*******************************************************************************
415    
416     Procedure:
417    
418     tcpListen ()
419    
420     Listen for Network Connection Requests from Clients.
421    
422    
423     Purpose:
424    
425     Function tcpListen() creates a "listening" endpoint on which a network
426     server can listen for connection requests from clients. The server
427     then calls tcpAnswer() to "answer" incoming requests.
428    
429    
430     Invocation:
431    
432     status = tcpListen (serverName, backlog, &listeningPoint) ;
433    
434     where
435    
436     <serverName> - I
437     is the server's name. This is used for determining the port
438     associated with the server (via the system's name/port mappings).
439     You can side-step the system maps and explicitly specify a
440     particular port by passing in a decimal number encoded in ASCII
441     (e.g., "1234" for port 1234).
442     <backlog> - I
443     is the number of connection requests that can be outstanding
444     for the server. UNIX systems typically allow a maximum of 5.
445     <listeningPoint> - O
446     returns a handle for the new listening endpoint.
447     <status> - O
448     returns the status of creating the endpoint: zero if there
449     were no errors and ERRNO otherwise.
450    
451     *******************************************************************************/
452    
453    
454     int tcpListen (
455    
456     # if __STDC__
457     const char *serverName,
458     int backlog,
459     TcpEndpoint *listeningPoint)
460     # else
461     serverName, backlog, listeningPoint)
462    
463     char *serverName ;
464     int backlog ;
465     TcpEndpoint *listeningPoint ;
466     # endif
467    
468     { /* Local variables. */
469     int optval, portNumber ;
470     struct sockaddr_in socketName ;
471    
472    
473    
474    
475    
476     if (serverName == NULL) serverName = "0" ;
477     if (backlog < 0) backlog = 99 ;
478    
479     /* Create an endpoint structure. */
480    
481     *listeningPoint = (TcpEndpoint) malloc (sizeof (_TcpEndpoint)) ;
482     if (*listeningPoint == NULL) {
483     vperror ("(tcpListen) Error allocating endpoint structure for %s.\nmalloc: ",
484     serverName) ;
485     return (errno) ;
486     }
487    
488     (*listeningPoint)->type = TcpListeningPoint ;
489     (*listeningPoint)->fd = -1 ;
490    
491     (*listeningPoint)->name = strdup (serverName) ;
492     if ((*listeningPoint)->name == NULL) {
493     vperror ("(tcpListen) Error duplicating server name: %s\nstrdup: ",
494     serverName) ;
495     CLEAN_UP (*listeningPoint) ;
496     return (errno) ;
497     }
498    
499     /* Lookup the port number bound to the server name. */
500    
501     portNumber = netPortOf (serverName, "tcp") ;
502     if (portNumber == -1) {
503     vperror ("(tcpListen) Error getting server entry for %s.\nnetPortOf: ",
504     serverName) ;
505     CLEAN_UP (*listeningPoint) ;
506     return (errno) ;
507     }
508    
509     /* Set up the network address for the connection. */
510    
511     memset (&socketName, '\0', sizeof socketName) ;
512     socketName.sin_family = AF_INET ;
513     socketName.sin_port = htons (portNumber) ;
514     socketName.sin_addr.s_addr = INADDR_ANY ;
515    
516     /* Create a socket for the connection. */
517    
518     (*listeningPoint)->fd = socket (AF_INET, SOCK_STREAM, 0) ;
519     if ((*listeningPoint)->fd < 0) {
520     vperror ("(tcpListen) Error creating listening socket for endpoint %s.\nsocket: ",
521     serverName) ;
522     CLEAN_UP (*listeningPoint) ;
523     return (errno) ;
524     }
525    
526     /* Configure the socket so it can be discarded quickly when no longer needed.
527     If the SO_REUSEADDR option is not enabled and server A goes down without
528     client B closing its half of the broken connection, server A can't come
529     back up again. Server A keeps getting an "address in use" error, even
530     though A is the "owner" of the port and B can't really do anything with
531     its broken connection! The reuse-address option allows A to come right
532     back up again and create a new listening socket. */
533    
534     optval = 1 ; /* Enable address reuse. */
535     if (setsockopt ((*listeningPoint)->fd, SOL_SOCKET, SO_REUSEADDR,
536     (char *) &optval, sizeof optval) == -1) {
537     vperror ("(tcpListen) Error setting %s endpoint's listening socket for re-use.\nsetsocketopt: ",
538     serverName) ;
539     CLEAN_UP (*listeningPoint) ;
540     return (errno) ;
541     }
542    
543     /* Bind the network address to the socket and enable it to listen for
544     connection requests. */
545    
546     if (bind ((*listeningPoint)->fd,
547     (struct sockaddr *) &socketName, sizeof socketName)) {
548     vperror ("(tcpListen) Error binding %s endpoint's socket name.\nbind: ",
549     serverName) ;
550     CLEAN_UP (*listeningPoint) ;
551     return (errno) ;
552     }
553    
554     if (portNumber == 0) {
555     portNumber = sktPort (serverName, (*listeningPoint)->fd) ;
556     free ((*listeningPoint)->name) ;
557     (*listeningPoint)->name = strndup (NULL, 16) ;
558     if ((*listeningPoint)->name == NULL) {
559     vperror ("(tcpListen) Error duplicating port name: %d\nstrndup: ",
560     portNumber) ;
561     CLEAN_UP (*listeningPoint) ;
562     }
563     sprintf ((*listeningPoint)->name, "%d", portNumber) ;
564     }
565    
566     if (listen ((*listeningPoint)->fd, backlog)) {
567     vperror ("(tcpListen) Error enabling acceptance of connection requests on %s endpoint.\nlisten: ",
568     (*listeningPoint)->name) ;
569     CLEAN_UP (*listeningPoint) ;
570     return (errno) ;
571     }
572    
573     if (tcp_util_debug)
574     printf ("(tcpListen) Listening on %s, port %d, socket %d.\n",
575     (*listeningPoint)->name, portNumber, (*listeningPoint)->fd) ;
576    
577     return (0) ;
578    
579     }
580    
581     /*******************************************************************************
582    
583     Procedure:
584    
585     tcpRequestPending ()
586    
587     Check a Listening Port for Pending Connection Requests.
588    
589    
590     Purpose:
591    
592     The tcpRequestPending() function checks to see if any connection requests
593     from potential clients are waiting to be answered.
594    
595    
596     Invocation:
597    
598     isPending = tcpRequestPending (listeningPoint) ;
599    
600     where
601    
602     <listeningPoint> - I
603     is the endpoint handle returned by tcpListen().
604     <isPending> - O
605     returns true (a non-zero value) if connection requests are
606     pending and false (zero) otherwise.
607    
608     *******************************************************************************/
609    
610    
611     int tcpRequestPending (
612    
613     # if __STDC__
614     TcpEndpoint listeningPoint)
615     # else
616     listeningPoint)
617    
618     TcpEndpoint listeningPoint ;
619     # endif
620    
621     { /* Local variables. */
622     fd_set readMask ;
623     struct timeval timeout ;
624    
625    
626    
627     if (listeningPoint == NULL) return (0) ;
628    
629     /* Poll the listening socket for input. */
630    
631     for ( ; ; ) {
632     FD_ZERO (&readMask) ; FD_SET (listeningPoint->fd, &readMask) ;
633     timeout.tv_sec = timeout.tv_usec = 0 ; /* No wait. */
634     if (select (listeningPoint->fd+1, &readMask, NULL, NULL, &timeout) >= 0)
635     break ;
636     if (errno == EINTR) continue ; /* Retry on signal interrupt. */
637     vperror ("(tcpRequestPending) Error polling endpoint %s, socket %d.\nselect: ",
638     listeningPoint->name, listeningPoint->fd) ;
639     return (0) ;
640     }
641    
642     return (FD_ISSET (listeningPoint->fd, &readMask)) ; /* Request pending? */
643    
644     }
645    
646     /*******************************************************************************
647    
648     Procedure:
649    
650     tcpCall ()
651    
652     Request a Network Connection to a Server.
653    
654    
655     Purpose:
656    
657     Function tcpCall() is used by a client task to "call" a server task
658     and request a network connection to the server. If its no-wait
659     argument is false (zero), tcpCall() waits until the connection is
660     established (or refused) before returning to the invoking function:
661    
662     #include "tcp_util.h" -- TCP/IP networking utilities.
663     TcpEndpoint connection ;
664     ...
665     if (tcpCall ("<server>[@<host>]", 0, &connection)) {
666     ... error establishing connection ...
667     } else {
668     ... connection is established ...
669     }
670    
671     If the no-wait argument is true (non-zero), tcpCall() initiates the
672     connection attempt and immediately returns; the application must then
673     invoke tcpComplete() to complete the connection attempt:
674    
675     if (tcpCall ("<server>[@<host>]", 1, &connection)) {
676     ... error initiating connection attempt ...
677     } else {
678     ... do something else ...
679     if (tcpComplete (connection, -1.0, 1)) {
680     ... error establishing connection ...
681     } else {
682     ... connection is established ...
683     }
684     }
685    
686     No-wait mode is useful in applications which monitor multiple I/O sources
687     using select(2). After tcpCall() returns, a to-be-established connection's
688     file descriptor can be retrieved with tcpFd() and placed in select(2)'s
689     write mask. When select(2) detects that the connection is writeable,
690     the application can call tcpComplete() to complete the connection.
691    
692     IMPLEMENTATION NOTE: Connecting to a server is usually accomplished
693     by creating an unbound socket and calling connect(), a system call, to
694     establish the connection. In order to implement the timeout capability,
695     the socket is configured for non-blocking I/O before connect() is called.
696     If the connection cannot be established right away, connect() returns
697     immediately and select() is called to wait for the timeout interval to
698     expire or for the socket to become writeable (indicating the connection
699     is complete). Once the connection is established, the socket is
700     reconfigured for blocking I/O.
701    
702     The basic idea for implementing a timeout capability was outlined in
703     a posting from W. Richard Stevens (*the* network guru) I retrieved
704     from one of the WWW news archives. VxWorks has a non-portable
705     connectWithTimeout() function which, to save a few "#ifdef"s, I don't
706     use.
707    
708    
709     Invocation:
710    
711     status = tcpCall (serverName, noWait, &dataPoint) ;
712    
713     where
714    
715     <serverName> - I
716     is the server's name: "<server>[@<host>]". The server can be
717     specified as a name or as a port number. The host, if given,
718     can be specified as a name or as a dotted Internet address.
719     <noWait> - I
720     specifies if tcpCall() should wait for a connection to be
721     established. If noWait is false (zero), tcpCall() waits
722     until a connection is established or refused before returning
723     control to the caller. If noWait is true (a non-zero value),
724     tcpCall() initiates a connection attempt and returns to the
725     caller immediately; the caller is responsible for eventually
726     calling tcpComplete() to complete the connection.
727     <dataPoint> - O
728     returns a handle for the endpoint.
729     <status> - O
730     returns the status of establishing the network connection:
731     zero if there were no errors and ERRNO otherwise.
732    
733     *******************************************************************************/
734    
735    
736     int tcpCall (
737    
738     # if __STDC__
739     const char *serverName,
740     int noWait,
741     TcpEndpoint *dataPoint)
742     # else
743     serverName, noWait, dataPoint)
744    
745     char *serverName ;
746     int noWait ;
747     TcpEndpoint *dataPoint ;
748     # endif
749    
750     { /* Local variables. */
751     char *s, hostName[MAXHOSTNAMELEN+1], server_name[MAXHOSTNAMELEN+1] ;
752     int length, optval, portNumber ;
753     struct sockaddr_in socketName ;
754    
755    
756    
757    
758    
759     /*******************************************************************************
760     Determine the host and server information needed to make the connection.
761     *******************************************************************************/
762    
763    
764     /* Parse the host and server names. If the host name is not defined
765     explicitly, it defaults to the local host. */
766    
767     s = netHostOf (netAddrOf (NULL)) ;
768     if (s == NULL) {
769     vperror ("(tcpCall) Error getting local host name.\nnetHostOf: ") ;
770     return (errno) ;
771     }
772     strcpy (hostName, s) ;
773    
774     s = strchr (serverName, '@') ;
775     if (s == NULL) { /* "<server>" */
776     strcpy (server_name, serverName) ;
777     } else { /* "<server>@<host>" */
778     length = s - serverName ;
779     strncpy (server_name, serverName, length) ;
780     server_name[length] = '\0' ;
781     strcpy (hostName, ++s) ;
782     }
783    
784    
785     /* Create an endpoint structure. */
786    
787     *dataPoint = (TcpEndpoint) malloc (sizeof (_TcpEndpoint)) ;
788     if (*dataPoint == NULL) {
789     vperror ("(tcpCall) Error allocating connection structure for %s.\nmalloc: ",
790     serverName) ;
791     return (errno) ;
792     }
793    
794     (*dataPoint)->type = TcpDataPoint ;
795     (*dataPoint)->fd = -1 ;
796    
797     (*dataPoint)->name = malloc (strlen (server_name) + 1 +
798     strlen (hostName) + 1) ;
799     if ((*dataPoint)->name == NULL) {
800     vperror ("(tcpCall) Error duplicating server name: %s@%s\nmalloc: ",
801     server_name, hostName) ;
802     CLEAN_UP (*dataPoint) ;
803     return (errno) ;
804     }
805     sprintf ((*dataPoint)->name, "%s@%s", server_name, hostName) ;
806    
807    
808     /* Lookup the port number bound to the server name. */
809    
810     portNumber = netPortOf (server_name, "tcp") ;
811     if (portNumber == -1) {
812     vperror ("(tcpCall) Error getting server entry for %s.\nnetPortOf: ",
813     serverName) ;
814     CLEAN_UP (*dataPoint) ;
815     return (errno) ;
816     }
817    
818    
819     /* Set up the network address for the connection. */
820    
821     memset (&socketName, '\0', sizeof socketName) ;
822     socketName.sin_family = AF_INET ;
823     socketName.sin_port = htons (portNumber) ;
824    
825     socketName.sin_addr.s_addr = netAddrOf (hostName) ;
826     if ((long) socketName.sin_addr.s_addr == -1) {
827     vperror ("(tcpCall) Error getting host entry for %s.\nnetAddrOf: ",
828     hostName) ;
829     CLEAN_UP (*dataPoint) ;
830     return (errno) ;
831     }
832    
833     /*******************************************************************************
834     Establish a connection with the server.
835     *******************************************************************************/
836    
837    
838     /* Create a socket for the connection. */
839    
840     (*dataPoint)->fd = socket (AF_INET, SOCK_STREAM, 0) ;
841     if ((*dataPoint)->fd < 0) {
842     vperror ("(tcpCall) Error creating socket for %s.\nsocket: ",
843     (*dataPoint)->name) ;
844     CLEAN_UP (*dataPoint) ;
845     return (errno) ;
846     }
847    
848    
849     /* Configure the socket so it can be discarded quickly when no longer needed.
850     (See the description of the SO_REUSEADDR option under NET_ANSWER, above.) */
851    
852     optval = 1 ; /* Enable address reuse. */
853     if (setsockopt ((*dataPoint)->fd, SOL_SOCKET, SO_REUSEADDR,
854     (char *) &optval, sizeof optval) == -1) {
855     vperror ("(tcpCall) Error setting %s's socket for re-use.\nsetsocketopt: ",
856     (*dataPoint)->name) ;
857     CLEAN_UP (*dataPoint) ;
858     return (errno) ;
859     }
860    
861    
862     /* If caller does not wish to wait for the connection to be established, then
863     configure the socket for non-blocking I/O. This causes the connect(2) call
864     to only initiate the attempt to connect; tcpComplete() must be called to
865     complete the connection. */
866    
867     optval = 1 ; /* A value of 1 enables non-blocking I/O. */
868     if (noWait && (ioctl ((*dataPoint)->fd, FIONBIO, &optval) == -1)) {
869     vperror ("(tcpCall) Error configuring %s's socket for non-blocking I/O.\nioctl: ",
870     (*dataPoint)->name) ;
871     CLEAN_UP (*dataPoint) ;
872     return (errno) ;
873     }
874    
875    
876     /* Attempt to establish the connection. */
877    
878     if (connect ((*dataPoint)->fd,
879     (struct sockaddr *) &socketName, sizeof socketName) &&
880     (!noWait || (errno != EINPROGRESS))) {
881     vperror ("(tcpCall) Error attempting to connect to %s.\nconnect: ",
882     (*dataPoint)->name) ;
883     CLEAN_UP (*dataPoint) ;
884     return (errno) ;
885     }
886    
887    
888     /* If caller does not wish to wait for the connection to be established,
889     then return immediately; the caller is responsible for subsequently
890     calling tcpComplete(). */
891    
892     if (noWait) return (0) ;
893    
894    
895     /* The connection has been established. Configure the socket so that the
896     operating system periodically verifies that the connection is still alive
897     by "pinging" the server. */
898    
899     optval = 1 ; /* Enable keep-alive transmissions. */
900     if (setsockopt ((*dataPoint)->fd, SOL_SOCKET, SO_KEEPALIVE,
901     (char *) &optval, sizeof optval) == -1) {
902     vperror ("(tcpCall) Error enabling keep-alive mode for connection to %s.\nsetsockopt: ",
903     (*dataPoint)->name) ;
904     CLEAN_UP (*dataPoint) ;
905     return (errno) ;
906     }
907    
908     if (tcp_util_debug) printf ("(tcpCall) Connected to %s, port %d, socket %d.\n",
909     (*dataPoint)->name,
910     sktPort ((*dataPoint)->name, (*dataPoint)->fd),
911     (*dataPoint)->fd) ;
912    
913    
914     return (0) ; /* Successful completion. */
915    
916     }
917    
918     /*******************************************************************************
919    
920     Procedure:
921    
922     tcpComplete ()
923    
924     Complete a Call to a Server.
925    
926    
927     Purpose:
928    
929     Function tcpComplete() waits for an asynchronous, network connection
930     attempt to complete. Invoking tcpCall() in no-wait mode initiates
931     an attempt to connect to a network server. At some later time, the
932     application must call tcpComplete() to complete the connection attempt
933     (if it is fated to complete). See tcpCall() for an example of using
934     tcpComplete().
935    
936     The default behavior of tcpComplete() is to NOT destroy the endpoint
937     created by tcpCall(), regardless of any errors. Since the endpoint
938     SHOULD be destroyed in the event of an error, the destroyOnError flag
939     provides a short-hand means of waiting for a connection without
940     explicitly destroying the endpoint if the connection attempt fails:
941    
942     #include "tcp_util.h" -- TCP/IP networking utilities.
943     TcpEndpoint connection ;
944     ...
945     if (tcpCall ("<server>[@<host>]", 1, &connection) ||
946     tcpComplete (connection, 30.0, 1)) {
947     ... timeout or error establishing connection;
948     connection is destroyed ...
949     } else {
950     ... connection is established ...
951     }
952    
953     Some applications, however, may not wish to destroy the connection right
954     away. For example, the following code fragment periodically displays an
955     in-progress message until a connection is established or refused:
956    
957     if (tcpCall ("<server>[@<host>]", 1, &connection)) {
958     ... error initiating connection attempt ...
959     } else {
960     for ( ; ; ) {
961     printf ("Waiting for %s ...\n", tcpName (connection)) ;
962     if (!tcpComplete (connection, 5.0, 0)) {
963     ... connection is established ...
964     break ;
965     } else if (errno != EWOULDBLOCK) {
966     ... connection attempt failed ...
967     tcpDestroy (connection) ; connection = NULL ;
968     break ;
969     }
970     }
971     }
972    
973    
974     Invocation:
975    
976     status = tcpComplete (dataPoint, timeout, destroyOnError) ;
977    
978     where
979    
980     <dataPoint> - I
981     is the endpoint handle returned by tcpCall().
982     <timeout> - I
983     specifies the maximum amount of time (in seconds) that the
984     caller wishes to wait for the call to complete. A fractional
985     time can be specified; e.g., 2.5 seconds. A negative timeout
986     (e.g., -1.0) causes an infinite wait; a zero timeout (0.0)
987     causes an immediate return if the connection is not yet
988     established.
989     <destroyOnError> - I
990     specifies if tcpComplete() should destroy the endpoint in the
991     event of an error. If this argument is true (a non-zero value),
992     tcpComplete() calls tcpDestroy() to destroy the endpoint. If
993     this argument is false (zero), the calling routine itself is
994     responsible for destroying the endpoint.
995     <status> - O
996     returns the status of completing the network connection:
997     zero if there were no errors, EWOULDBLOCK if the timeout
998     interval expired before the connection was established,
999     and ERRNO otherwise.
1000    
1001     *******************************************************************************/
1002    
1003    
1004     int tcpComplete (
1005    
1006     # if __STDC__
1007     TcpEndpoint dataPoint,
1008     double timeout,
1009     int destroyOnError)
1010     # else
1011     dataPoint, timeout, destroyOnError)
1012    
1013     TcpEndpoint dataPoint ;
1014     double timeout ;
1015     int destroyOnError ;
1016     # endif
1017    
1018     { /* Local variables. */
1019     fd_set writeMask ;
1020     int length, numActive, optval ;
1021     struct timeval deltaTime, expirationTime ;
1022    
1023    
1024    
1025    
1026    
1027     /* If a timeout interval was specified, then compute the expiration time
1028     of the interval as the current time plus the interval. */
1029    
1030     if (timeout >= 0.0)
1031     expirationTime = tvAdd (tvTOD (), tvCreateF (timeout)) ;
1032    
1033    
1034     /* Wait for the call to complete, which is indicated by the connection
1035     being writeable. */
1036    
1037     for ( ; ; ) {
1038     if (timeout >= 0.0) deltaTime = tvSubtract (expirationTime, tvTOD ()) ;
1039     FD_ZERO (&writeMask) ; FD_SET (dataPoint->fd, &writeMask) ;
1040     numActive = select (dataPoint->fd+1, NULL, &writeMask, NULL,
1041     (timeout < 0.0) ? NULL : &deltaTime) ;
1042     if (numActive > 0) break ;
1043     if (numActive == 0) errno = EWOULDBLOCK ;
1044     if (errno == EINTR) continue ; /* Retry on signal interrupt. */
1045     vperror ("(tcpComplete) Error waiting to connect to %s.\nselect: ",
1046     dataPoint->name) ;
1047     if (destroyOnError) CLEAN_UP (dataPoint) ;
1048     return (errno) ;
1049     }
1050    
1051    
1052     /* Check the connection's error status. */
1053    
1054     length = sizeof optval ;
1055     if (getsockopt (dataPoint->fd, SOL_SOCKET, SO_ERROR,
1056     (char *) &optval, &length) == -1) {
1057     vperror ("(tcpComplete) Error checking error status of connection to %s.\ngetsockopt: ",
1058     dataPoint->name) ;
1059     if (destroyOnError) CLEAN_UP (dataPoint) ;
1060     return (errno) ;
1061     }
1062    
1063     if (optval) {
1064     errno = optval ;
1065     vperror ("(tcpComplete) Error connecting to %s.\nconnect: ",
1066     dataPoint->name) ;
1067     if (destroyOnError) CLEAN_UP (dataPoint) ;
1068     return (errno) ;
1069     }
1070    
1071    
1072     /* The connection has been established. Configure the socket so that the
1073     operating system periodically verifies that the connection is still alive
1074     by "pinging" the server. */
1075    
1076     optval = 1 ; /* Enable keep-alive transmissions. */
1077     if (setsockopt (dataPoint->fd, SOL_SOCKET, SO_KEEPALIVE,
1078     (char *) &optval, sizeof optval) == -1) {
1079     vperror ("(tcpComplete) Error enabling keep-alive mode for connection to %s.\nsetsockopt: ",
1080     dataPoint->name) ;
1081     if (destroyOnError) CLEAN_UP (dataPoint) ;
1082     return (errno) ;
1083     }
1084    
1085    
1086     /* Reconfigure the socket for blocking I/O. */
1087    
1088     optval = 0 ; /* A value of 0 sets blocking I/O. */
1089     if (ioctl (dataPoint->fd, FIONBIO, &optval) == -1) {
1090     vperror ("(tcpComplete) Error reconfiguring %s's socket for blocking I/O.\nioctl: ",
1091     dataPoint->name) ;
1092     if (destroyOnError) CLEAN_UP (dataPoint) ;
1093     return (errno) ;
1094     }
1095    
1096     if (tcp_util_debug) printf ("(tcpComplete) Connected to %s, port %d, socket %d.\n",
1097     dataPoint->name,
1098     sktPort (dataPoint->name, dataPoint->fd),
1099     dataPoint->fd) ;
1100    
1101    
1102     return (0) ; /* Successful completion. */
1103    
1104     }
1105    
1106     /*******************************************************************************
1107    
1108     Procedure:
1109    
1110     tcpIsReadable ()
1111    
1112     Check if Data is Waiting to be Read.
1113    
1114    
1115     Purpose:
1116    
1117     The tcpIsReadable() function checks to see if data is waiting to
1118     be read from a connection.
1119    
1120    
1121     Invocation:
1122    
1123     isReadable = tcpIsReadable (dataPoint) ;
1124    
1125     where
1126    
1127     <dataPoint> - I
1128     is the endpoint handle returned by tcpAnswer() or tcpCall().
1129     <isReadable> - O
1130     returns true (a non-zero value) if data is available for
1131     reading and false (zero) otherwise.
1132    
1133     *******************************************************************************/
1134    
1135    
1136     int tcpIsReadable (
1137    
1138     # if __STDC__
1139     TcpEndpoint dataPoint)
1140     # else
1141     dataPoint)
1142    
1143     TcpEndpoint dataPoint ;
1144     # endif
1145    
1146     {
1147     if (dataPoint == NULL)
1148     return (0) ;
1149     else
1150     return (sktIsReadable (dataPoint->name, dataPoint->fd)) ;
1151     }
1152    
1153     /*******************************************************************************
1154    
1155     Procedure:
1156    
1157     tcpIsUp ()
1158    
1159     Check if a Connection is Up.
1160    
1161    
1162     Purpose:
1163    
1164     The tcpIsUp() function checks to see if a network connection is still up.
1165    
1166    
1167     Invocation:
1168    
1169     isUp = tcpIsUp (dataPoint) ;
1170    
1171     where
1172    
1173     <dataPoint> - I
1174     is the endpoint handle returned by tcpAnswer() or tcpCall().
1175     <isUp> - O
1176     returns true (a non-zero value) if the network connection is
1177     up and false (zero) otherwise.
1178    
1179     *******************************************************************************/
1180    
1181    
1182     int tcpIsUp (
1183    
1184     # if __STDC__
1185     TcpEndpoint dataPoint)
1186     # else
1187     dataPoint)
1188    
1189     TcpEndpoint dataPoint ;
1190     # endif
1191    
1192     {
1193     if (dataPoint == NULL)
1194     return (0) ;
1195     else
1196     return (sktIsUp (dataPoint->name, dataPoint->fd)) ;
1197     }
1198    
1199     /*******************************************************************************
1200    
1201     Procedure:
1202    
1203     tcpIsWriteable ()
1204    
1205     Check if Data can be Written.
1206    
1207    
1208     Purpose:
1209    
1210     The tcpIsWriteable() function checks to see if data can be written
1211     to a connection.
1212    
1213    
1214     Invocation:
1215    
1216     isWriteable = tcpIsWriteable (dataPoint) ;
1217    
1218     where
1219    
1220     <dataPoint> - I
1221     is the endpoint handle returned by tcpAnswer() or tcpCall().
1222     <isWriteable> - O
1223     returns true (a non-zero value) if data connection is ready
1224     for writing and false (zero) otherwise.
1225    
1226     *******************************************************************************/
1227    
1228    
1229     int tcpIsWriteable (
1230    
1231     # if __STDC__
1232     TcpEndpoint dataPoint)
1233     # else
1234     dataPoint)
1235    
1236     TcpEndpoint dataPoint ;
1237     # endif
1238    
1239     {
1240     if (dataPoint == NULL)
1241     return (0) ;
1242     else
1243     return (sktIsWriteable (dataPoint->name, dataPoint->fd)) ;
1244     }
1245    
1246     /*******************************************************************************
1247    
1248     Procedure:
1249    
1250     tcpRead ()
1251    
1252     Read Data from a Network Connection.
1253    
1254    
1255     Purpose:
1256    
1257     Function tcpRead() reads data from a network connection. Because of
1258     the way network I/O works, a single record written to a connection
1259     by one task may be read in multiple "chunks" by the task at the other
1260     end of the connection. For example, a 100-byte record written by a
1261     client may be read by the server in two chunks, one of 43 bytes and
1262     the other of 57 bytes. tcpRead() takes this into account and, if
1263     you ask it for 100 bytes, it will automatically perform however many
1264     network reads are necessary to collect the 100 bytes. You can also
1265     ask tcpRead() to return the first chunk received, whatever its length;
1266     see the numBytesToRead argument.
1267    
1268     A timeout can be specified that limits how long tcpRead() waits for the
1269     first data to arrive. If a complete record must be read (see above),
1270     tcpRead() will then wait as long as necessary for the remainder of the
1271     record to be received. This ensures that a partial record will NOT be
1272     returned at the end of the timeout interval.
1273    
1274    
1275     Invocation:
1276    
1277     status = tcpRead (dataPoint, timeout, numBytesToRead,
1278     buffer, &numBytesRead) ;
1279    
1280     where
1281    
1282     <dataPoint> - I
1283     is the endpoint handle returned by tcpAnswer() or tcpCall().
1284     <timeout> - I
1285     specifies the maximum amount of time (in seconds) that the caller
1286     wishes to wait for the first data to arrive. A fractional time can
1287     be specified; e.g., 2.5 seconds. A negative timeout (e.g., -1.0)
1288     causes an infinite wait; a zero timeout (0.0) allows a read only
1289     if input is immediately available.
1290     <numBytesToRead> - I
1291     has two different meanings depending on its sign:
1292     (1) If the number of bytes to read is positive, tcpRead()
1293     will continue to read input until it has accumulated
1294     the exact number of bytes requested. If the timeout
1295     interval expires before ANY data has been read, then
1296     tcpRead() returns with an EWOULDBLOCK status. If some
1297     of the data is read before the timeout interval expires,
1298     tcpRead() will wait as long as necessary to read the
1299     remaining data. This ensures that a partial record is
1300     not returned to the caller at the end of the timeout
1301     interval.
1302     (2) If the number of bytes to read is negative, tcpRead()
1303     returns after reading the first "chunk" of input received;
1304     the number of bytes read from that first "chunk" is limited
1305     by the absolute value of numBytesToRead. A normal status
1306     (0) is returned if the first "chunk" of input is received
1307     before the timeout interval expires; EWOULDBLOCK is
1308     returned if no input is received within that interval.
1309     <buffer> - O
1310     receives the input data. This buffer should be at least
1311     numBytesToRead in size.
1312     <numBytesRead> - O
1313     returns the actual number of bytes read.
1314     <status> - O
1315     returns the status of reading from the network connection:
1316     zero if no errors occurred, EWOULDBLOCK if the timeout
1317     interval expired before the requested amount of data was
1318     input, and ERRNO otherwise. (See the numBytesToRead
1319     argument for a description of how that argument affects
1320     the returned status code.)
1321    
1322     *******************************************************************************/
1323    
1324    
1325     int tcpRead (
1326    
1327     # if __STDC__
1328     TcpEndpoint dataPoint,
1329     double timeout,
1330     int numBytesToRead,
1331     char *buffer,
1332     int *numBytesRead)
1333     # else
1334     dataPoint, timeout, numBytesToRead, buffer, numBytesRead)
1335    
1336     TcpEndpoint dataPoint ;
1337     double timeout ;
1338     int numBytesToRead ;
1339     char *buffer ;
1340     int *numBytesRead ;
1341     # endif
1342    
1343     { /* Local variables. */
1344     fd_set readMask ;
1345     int firstInputOnly, length, numActive ;
1346     struct timeval deltaTime, expirationTime ;
1347    
1348    
1349    
1350    
1351    
1352     if (dataPoint == NULL) {
1353     errno = EINVAL ;
1354     vperror ("(tcpRead) NULL endpoint handle: ") ;
1355     return (errno) ;
1356     }
1357    
1358     if (dataPoint->fd < 0) {
1359     errno = EINVAL ;
1360     vperror ("(tcpRead) %d file descriptor: ", dataPoint->fd) ;
1361     return (errno) ;
1362     }
1363    
1364     if (numBytesRead != NULL) *numBytesRead = 0 ;
1365    
1366    
1367     /*******************************************************************************
1368     If a timeout interval was specified, then wait until the expiration of
1369     the interval for data to be received.
1370     *******************************************************************************/
1371    
1372     if (timeout >= 0.0) {
1373    
1374     /* Compute the expiration time as the current time plus the interval. */
1375    
1376     expirationTime = tvAdd (tvTOD (), tvCreateF (timeout)) ;
1377    
1378     /* Wait for data to arrive. */
1379    
1380     for ( ; ; ) {
1381     deltaTime = tvSubtract (expirationTime, tvTOD ()) ;
1382     FD_ZERO (&readMask) ; FD_SET (dataPoint->fd, &readMask) ;
1383     numActive = select (dataPoint->fd+1, &readMask, NULL, NULL,
1384     &deltaTime) ;
1385     if (numActive >= 0) break ;
1386     if (errno == EINTR) continue ; /* Retry on signal interrupt. */
1387     vperror ("(tcpRead) Error waiting for input on %s, socket %d.\nselect: ",
1388     dataPoint->name, dataPoint->fd) ;
1389     return (errno) ;
1390     }
1391    
1392     if (numActive == 0) {
1393     errno = EWOULDBLOCK ;
1394     #ifdef REPORT_TIMEOUT
1395     vperror ("(tcpRead) Timeout while waiting for input on %s, socket %d.\n",
1396     dataPoint->name, dataPoint->fd) ;
1397     #endif
1398     return (errno) ;
1399     }
1400    
1401     }
1402    
1403    
1404     /*******************************************************************************
1405     Read the requested amount of data from the network connection.
1406     *******************************************************************************/
1407    
1408     firstInputOnly = (numBytesToRead < 0) ;
1409     if (firstInputOnly) numBytesToRead = abs (numBytesToRead) ;
1410    
1411     while (numBytesToRead > 0) {
1412    
1413     length = read (dataPoint->fd, buffer, numBytesToRead) ;
1414     if (length < 0) {
1415     vperror ("(tcpRead) Error reading from connection %d.\nread: ",
1416     dataPoint->fd) ;
1417     return (errno) ;
1418     } else if (length == 0) {
1419     errno = EPIPE ;
1420     vperror ("(tcpRead) Broken connection on %s, socket %d.\nread: ",
1421     dataPoint->name, dataPoint->fd) ;
1422     return (errno) ;
1423     }
1424    
1425     if (tcp_util_debug) {
1426     printf ("(tcpRead) Read %d bytes from %s, socket %d.\n",
1427     length, dataPoint->name, dataPoint->fd) ;
1428     meoDumpX (stdout, " ", 0, buffer, length) ;
1429     }
1430    
1431     if (numBytesRead != NULL) *numBytesRead += length ;
1432     numBytesToRead -= length ;
1433     buffer += length ;
1434    
1435     if (firstInputOnly) break ;
1436    
1437     }
1438    
1439    
1440     return (0) ;
1441    
1442     }
1443    
1444     /*******************************************************************************
1445    
1446     Procedure:
1447    
1448     tcpWrite ()
1449    
1450     Write Data to a Network Connection.
1451    
1452    
1453     Purpose:
1454    
1455     Function tcpWrite() writes data to a network connection. Because
1456     of the way network I/O works, attempting to output a given amount
1457     of data to a network connection may require multiple system
1458     WRITE(2)s. For example, when called to output 100 bytes of data,
1459     WRITE(2) may return after only outputting 64 bytes of data; the
1460     application is responsible for re-calling WRITE(2) to output the
1461     other 36 bytes. tcpWrite() takes this into account and, if you
1462     ask it to output 100 bytes, it will call WRITE(2) as many times
1463     as necessary to output the full 100 bytes of data to the connection.
1464    
1465     A timeout interval can be specified that limits how long tcpWrite()
1466     waits to output the desired amount of data. If the timeout interval
1467     expires before all the data has been written, tcpWrite() returns the
1468     number of bytes actually output in the numBytesWritten argument.
1469    
1470    
1471     Invocation:
1472    
1473     status = tcpWrite (dataPoint, timeout, numBytesToWrite, buffer,
1474     &numBytesWritten) ;
1475    
1476     where
1477    
1478     <dataPoint> - I
1479     is the endpoint handle returned by tcpAnswer() or tcpCall().
1480     <timeout> - I
1481     specifies the maximum amount of time (in seconds) that the
1482     caller wishes to wait for the data to be output. A fractional
1483     time can be specified; e.g., 2.5 seconds. A negative timeout
1484     (e.g., -1.0) causes an infinite wait; tcpWrite() will wait as
1485     long as necessary to output all of the data. A zero timeout
1486     (0.0) specifies no wait: if the socket is not ready for writing,
1487     tcpWrite() returns immediately; if the socket is ready for
1488     writing, tcpWrite() returns after outputting whatever it can.
1489     <numBytesToWrite> - I
1490     is the number of bytes to write. If the timeout argument
1491     indicates an infinite wait, tcpWrite() won't return until
1492     it has output the entire buffer of data. If the timeout
1493     argument is greater than or equal to zero, tcpWrite() will
1494     output as much as it can in the specified time interval,
1495     up to a maximum of numBytesToWrite.
1496     <buffer> - O
1497     is the data to be output.
1498     <numBytesWritten> - O
1499     returns the actual number of bytes written. If an infinite wait
1500     was specified (TIMEOUT < 0.0), then this number should equal
1501     numBytesToWrite. If a finite wait was specified, the number
1502     of bytes written may be less than the number requested.
1503     <status> - O
1504     returns the status of writing to the network connection:
1505     zero if no errors occurred, EWOULDBLOCK if the timeout
1506     interval expired before the entire buffer of data was
1507     output, and ERRNO otherwise.
1508    
1509     *******************************************************************************/
1510    
1511    
1512     int tcpWrite (
1513    
1514     # if __STDC__
1515     TcpEndpoint dataPoint,
1516     double timeout,
1517     int numBytesToWrite,
1518     const char *buffer,
1519     int *numBytesWritten)
1520     # else
1521     dataPoint, timeout, numBytesToWrite, buffer, numBytesWritten)
1522    
1523     TcpEndpoint dataPoint ;
1524     double timeout ;
1525     int numBytesToWrite ;
1526     const char *buffer ;
1527     int *numBytesWritten ;
1528     # endif
1529    
1530     { /* Local variables. */
1531     fd_set writeMask ;
1532     int length, numActive ;
1533     struct timeval deltaTime, expirationTime ;
1534    
1535    
1536    
1537    
1538    
1539     if (dataPoint == NULL) {
1540     errno = EINVAL ;
1541     vperror ("(tcpWrite) NULL endpoint handle: ") ;
1542     return (errno) ;
1543     }
1544    
1545     if (dataPoint->fd < 0) {
1546     errno = EINVAL ;
1547     vperror ("(tcpWrite) %d file descriptor: ", dataPoint->fd) ;
1548     return (errno) ;
1549     }
1550    
1551    
1552     /* If a timeout interval was specified, then compute the expiration time
1553     of the interval as the current time plus the interval. */
1554    
1555     if (timeout >= 0.0)
1556     expirationTime = tvAdd (tvTOD (), tvCreateF (timeout)) ;
1557    
1558    
1559     /*******************************************************************************
1560     While the timeout interval has not expired, attempt to write the entire
1561     buffer of data to the network connection.
1562     *******************************************************************************/
1563    
1564     if (numBytesWritten != NULL) *numBytesWritten = 0 ;
1565    
1566     while (numBytesToWrite > 0) {
1567    
1568     /* Wait for the connection to be ready for writing. */
1569    
1570     for ( ; ; ) {
1571     if (timeout >= 0.0)
1572     deltaTime = tvSubtract (expirationTime, tvTOD ()) ;
1573     FD_ZERO (&writeMask) ; FD_SET (dataPoint->fd, &writeMask) ;
1574     numActive = select (dataPoint->fd+1, NULL, &writeMask, NULL,
1575     (timeout < 0.0) ? NULL : &deltaTime) ;
1576     if (numActive >= 0) break ;
1577     if (errno == EINTR) continue ; /* Retry on signal interrupt. */
1578     vperror ("(tcpWrite) Error waiting to write data to %s.\nselect: ",
1579     dataPoint->name) ;
1580     return (errno) ;
1581     }
1582    
1583     if (numActive == 0) {
1584     errno = EWOULDBLOCK ;
1585     vperror ("(tcpWrite) Timeout while waiting to write data to %s.\n",
1586     dataPoint->name) ;
1587     return (errno) ;
1588     }
1589    
1590     /* Write the next chunk of data to the network. */
1591    
1592     length = write (dataPoint->fd, (char *) buffer, numBytesToWrite) ;
1593     if (length < 0) {
1594     vperror ("(tcpWrite) Error writing to %s.\nwrite: ",
1595     dataPoint->name) ;
1596     return (errno) ;
1597     }
1598    
1599     if (tcp_util_debug) {
1600     printf ("(tcpWrite) Wrote %d bytes to %s, socket %d.\n",
1601     length, dataPoint->name, dataPoint->fd) ;
1602     meoDumpX (stdout, " ", 0, buffer, length) ;
1603     }
1604    
1605     if (numBytesWritten != NULL) *numBytesWritten += length ;
1606     numBytesToWrite -= length ;
1607     buffer += length ;
1608    
1609     }
1610    
1611    
1612     return (0) ;
1613    
1614     }
1615    
1616     /*******************************************************************************
1617    
1618     Procedure:
1619    
1620     tcpDestroy ()
1621    
1622     Close a Network Endpoint.
1623    
1624    
1625     Purpose:
1626    
1627     Function tcpDestroy() deletes listening endpoints created by tcpListen()
1628     and data endpoints created by tcpAnswer() or tcpCall().
1629    
1630    
1631     Invocation:
1632    
1633     status = tcpDestroy (endpoint) ;
1634    
1635     where
1636    
1637     <endpoint> - I
1638     is the endpoint handle returned by tcpListen(), tcpAnswer(),
1639     or tcpCall().
1640     <status> - O
1641     returns the status of deleting the endpoint, zero if there were
1642     no errors and ERRNO otherwise.
1643    
1644     *******************************************************************************/
1645    
1646    
1647     int tcpDestroy (
1648    
1649     # if __STDC__
1650     TcpEndpoint endpoint)
1651     # else
1652     endpoint)
1653    
1654     TcpEndpoint endpoint ;
1655     # endif
1656    
1657     {
1658    
1659     if (endpoint == NULL) return (0) ;
1660    
1661     if (tcp_util_debug) printf ("(tcpDestroy) Closing %s, socket %d.\n",
1662     endpoint->name, endpoint->fd) ;
1663    
1664     /* Close the endpoint's socket. */
1665    
1666     if (endpoint->fd >= 0) close (endpoint->fd) ;
1667    
1668     /* Deallocate the endpoint structure. */
1669    
1670     if (endpoint->name != NULL) free (endpoint->name) ;
1671     free (endpoint) ;
1672    
1673     return (0) ;
1674    
1675     }
1676    
1677     /*******************************************************************************
1678    
1679     Procedure:
1680    
1681     tcpFd ()
1682    
1683     Get an Endpoint's Socket.
1684    
1685    
1686     Purpose:
1687    
1688     Function tcpFd() returns a listening or data endpoint's socket.
1689    
1690    
1691     Invocation:
1692    
1693     fd = tcpFd (endpoint) ;
1694    
1695     where
1696    
1697     <endpoint> - I
1698     is the endpoint handle returned by tcpListen(), tcpAnswer(),
1699     or tcpCall().
1700     <fd> - O
1701     returns the UNIX file descriptor for the endpoint's socket.
1702    
1703     *******************************************************************************/
1704    
1705    
1706     int tcpFd (
1707    
1708     # if __STDC__
1709     TcpEndpoint endpoint)
1710     # else
1711     endpoint)
1712    
1713     TcpEndpoint endpoint ;
1714     # endif
1715    
1716     {
1717     return ((endpoint == NULL) ? -1 : endpoint->fd) ;
1718     }
1719    
1720     /*******************************************************************************
1721    
1722     Procedure:
1723    
1724     tcpName ()
1725    
1726     Get an Endpoint's Name.
1727    
1728    
1729     Purpose:
1730    
1731     Function tcpName() returns a listening or data endpoint's name.
1732    
1733    
1734     Invocation:
1735    
1736     name = tcpName (endpoint) ;
1737    
1738     where
1739    
1740     <endpoint> - I
1741     is the endpoint handle returned by tcpListen(), tcpAnswer(),
1742     or tcpCall().
1743     <name> - O
1744     returns the endpoint's name. The name is stored in memory local
1745     to the TCP utilities and it should not be modified or freed
1746     by the caller.
1747    
1748     *******************************************************************************/
1749    
1750    
1751     char *tcpName (
1752    
1753     # if __STDC__
1754     TcpEndpoint endpoint)
1755     # else
1756     endpoint)
1757    
1758     TcpEndpoint endpoint ;
1759     # endif
1760    
1761     {
1762     if (endpoint == NULL) return ("") ;
1763     if (endpoint->name == NULL) return ("") ;
1764     return (endpoint->name) ;
1765     }
1766     </XMP></BODY></HTML>