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

# Content
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>