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