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