1<HTML>
2<HEAD>
3   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
4   <META NAME="Author" CONTENT="Mike Gleason">
5   <META NAME="GENERATOR" CONTENT="Mozilla/4.03 [en] (WinNT; I) [Netscape]">
6   <TITLE>sio: SocketIO Library</TITLE>
7</HEAD>
8<BODY BGCOLOR="#FFFFFF">
9
10<H1>
11SocketIO Library Documentation</H1>
12
13<UL>
14<LI>
15<A HREF="#Introduction">Introduction</A></LI>
16
17<LI>
18<A HREF="#Installation">Installation</A></LI>
19
20<LI>
21<A HREF="#Functions">Function Reference</A></LI>
22
23<LI>
24<A HREF="#Sample">Sample Code</A></LI>
25</UL>
26The purpose of the <I>SocketIO Library</I> (<I>sio</I> for short) is to
27provide a safe and consistent wrapper interface to a BSD Sockets implementation.&nbsp;
28The library also takes steps to protect against a few common pitfalls that
29plague poorly written BSD Sockets programs.&nbsp; Specifically, it was
30designed to do the following:
31
32
33<UL>
34<LI>
35Take care of reading and writing the full operation.
36
37<P><FONT SIZE=-1>For example, a network <TT>write()</TT> for 50 bytes may
38only write 30 bytes; <I>Sio</I> would continue writing until all 50 bytes
39have been sent.</FONT>
40<P></LI>
41
42<LI>
43<P>
44Allow operations that would normally block can be set to timeout after
45a customizable period of time.<P></LI>
46
47<LI>
48<P>
49Catch the SIGPIPE signal which would cause an unexpecting process to exit.<P>
50
51<P><FONT SIZE=-1>A frequent source of problems for beginning programmers
52is the situation where their process is suddenly no longer running because
53a write to a socket caused a broken pipe.  This problem can be difficult
54to diagnose if the process is running as a daemon process in the background.</FONT>
55<P>
56</LI>
57
58<LI>
59<P>
60Resume operation when system calls are interrupted (operations errout
61with <TT>EINTR</TT>).<P></LI>
62
63<LI>
64<P>
65Cater to the internet socket interface (i.e. <TT>struct sockaddr_in</TT>).<P></LI>
66
67<LI>
68<P>
69Simplified interface to name service.
70
71
72<P><FONT SIZE=-1>Library routines can take a textual address specification
73instead of you having to prepare a <TT>struct sockaddr_in</TT>.</FONT>
74<P>
75</LI>
76
77</UL>
78The library was written by Mike Gleason.  The first incarnation dates
79back to some time in 1992. The library may be used and distributed
80freely, as long as you give due credit.
81<P>
82<H3>
83<A NAME="Introduction"></A>Introduction</H3>
84The reader is assumed to be familiar with BSD Sockets.&nbsp; An excellent
85source of information is W. Richard Stevens' <U>UNIX Network Programming,
86Volume 1, Second Edition: Networking APIs: Sockets and XTI</U>, Prentice
87Hall, 1998, ISBN 0-13-490012-X.
88<BR>&nbsp;
89<H4>
90<A NAME="Connection_modes"></A>Connection modes</H4>
91A communications exchange between two end-points can be connection-oriented
92or connectionless.&nbsp; An connection-oriented exchange is characterized
93by a connection establishment, a series of messages, and a disconnection.&nbsp;
94A connectionless exchange is characterized by one or more independent messages.&nbsp;
95An analogy to a connection-oriented exchange would be a telephone call
96where one party establishes a connection to another, and a conversation
97ensues.&nbsp; A connectionless exchange analogy would be a letter sent
98via the postal service -- even if a writer sends two letters to the same
99recipient, each letter is transported independently.
100<BR>&nbsp;
101<H4>
102<A NAME="Message_modes"></A>Message modes</H4>
103For the <I>sio</I> library, all connection-oriented exchanges are data
104streams using the <I>TCP/IP</I> protocol.&nbsp; After connection establishment,
105the conversation consists of one long sequence of data bytes, which is
106known as a <I>stream</I>.&nbsp; Therefore there is no concept of a record
107boundary associated with a stream connection at the transport level, although
108often there is a record associated over the stream at the application level.&nbsp;
109The important thing here is to realize that there is no flow control associated
110with the stream, so although the data bytes are guaranteed to arrive in
111order, there is no guarantee that messages will be read in the same size
112blocks as they were originally written.
113
114<P>For connectionless exchanges, <I>sio</I> uses the <I>UDP/IP</I> protocol's
115datagram messages.&nbsp; There is an implicit definition of a record boundary,
116because each message is treated as a record. So, three writes results in
117three separate messages.
118
119<P>For example, let's say a sender writes three 50-byte messages.&nbsp;
120If a receiver does a 20-byte read followed by 100-byte read followed by
121a 50-byte read, the receiver would get the first 20 bytes of the first
122message, 50 of 50 bytes of the second message, and 50 of 50 bytes of the
123third message.&nbsp; It's important to understand that in the first read
124that since UDP datagrams are message oriented that the remaining 30 bytes
125of the first message are lost, and that although the second read is for
126100 bytes, the read immediately returns as finished even though only 50
127bytes were actually read.
128
129<P>With UDP datagrams, there are also other important implications that
130affect message ordering, duplication, and overall reliability.
131<BR>&nbsp;
132<H4>
133<A NAME="Creating_and_disposing"></A>Creating and disposing sockets</H4>
134A socket is an I/O descriptor that is associated with one side of a communications
135exchange.&nbsp; A socket must be either a client or a server, although
136after communications is established the difference is arbitrary.&nbsp;
137A server socket is one that waits for contact to be initiated by a client
138on a mutually agreed upon address and port number.&nbsp; A client socket
139initiates the first communication by sending to an existing server socket.&nbsp;
140Client and server sockets may of course be on different machines, and different
141networks altogether.
142
143<P>A stream server socket is created using <TT><A HREF="#SNewStreamServer">SNewStreamServer()</A></TT>,
144which returns a socket file descriptor ready to accept new client connections.&nbsp;
145After a socket descriptor is obtained, your server program can then use
146<TT><A HREF="#SAccept">SAccept()</A></TT> to establish a connection with
147a client.
148
149<P>A stream client socket is created using <TT><A HREF="#SNewStreamClient">SNewStreamClient()</A></TT>.&nbsp;
150After a socket descriptor is obtained, your client program can use <TT><A HREF="#SConnectByName">SConnectByName()</A></TT>
151or <TT><A HREF="#SConnect">SConnect()</A></TT> to initiate a connection
152to a server.
153
154<P>A datagram server socket is created using <TT><A HREF="#SNewDatagramServer">SNewDatagramServer()</A></TT>.&nbsp;
155After a socket descriptor is obtained, it is ready to receive messages
156from clients using <TT><A HREF="#SRecvfrom">SRecvfrom()</A></TT> and reply
157back with <TT><A HREF="#SSendto">SSendto()</A></TT>.
158
159<P>A datagram client socket is created using <TT><A HREF="#SNewDatagramClient">SNewDatagramClient()</A></TT>.&nbsp;
160After a socket descriptor is obtained, it is ready to communicate with
161servers using <TT><A HREF="#SSendto">SSendto()</A></TT> and <TT><A HREF="#SRecvfrom">SRecvfrom()</A></TT>.
162
163<P>All socket descriptors are disposed of with <TT><A HREF="#SClose">SClose()</A></TT>
164when communication is finished.
165<BR>&nbsp;
166<H4>
167<A NAME="SocketIO"></A>Socket I/O</H4>
168A stream connection uses <TT><A HREF="#SRead">SRead()</A></TT> to receive
169data from the stream and <TT><A HREF="#SWrite">SWrite()</A></TT> to send
170data.&nbsp; A datagram socket should use <TT><A HREF="#SRecvfrom">SRecvfrom()</A></TT>
171to read a message and <TT><A HREF="#SSendtoByName">SSendtoByName()</A></TT>
172or <TT><A HREF="#SSendto">SSendto()</A></TT> to send a message.&nbsp; Since
173each datagram communication is independent, these routines use an address
174specifier along with each call.
175<BR>&nbsp;
176<H3>
177<A NAME="Installation"></A>Installation</H3>
178First, unpack the archive. If the package arrived as a '.tar.gz' or '.tgz'
179file, you need to run gunzip, and then tar to extract the source files.
180You can do that in one shot by doing "<TT>gunzip -c sio.tgz | tar xvf -</TT>".
181If the package you have is a '.tar' file you can use "<TT>tar xvf sio.tar</TT>".
182If the package you have is a '.zip' file you can use "<TT>unzip sio.zip</TT>"
183or "<TT>pkunzip sio.zip</TT>".
184
185<P>Now go to the "<TT>sio</TT>" directory you just made. There is a script
186you must run which will checks your system for certain features, so that
187the library can be compiled on a variety of U<FONT SIZE=-1>NIX</FONT> systems.
188Run this script by typing "<TT>sh ./configure</TT>" in that directory.
189After that, you can look at the <TT>Makefile</TT> it made if you like,
190and then you run "<TT>make</TT>" to create the "<TT>libsio.a</TT>" library
191file.
192
193<P>Finally, install the library and headers. You can manually copy the
194files, or you can run "<TT>make install</TT>" to copy the files for you.
195If you choose to "<TT>make install</TT>" you may want to edit the <TT>Makefile</TT>
196if you do not want to install to the <TT>/usr/local</TT> tree.
197<BR>&nbsp;
198<H3>
199<A NAME="Functions"></A>Function Reference</H3>
200
201<UL>
202<table width="100%">
203	<tr>
204		<td><A HREF="#AddrStrToAddr">AddrStrToAddr</A></td>
205		<td><A HREF="#SConnectByName">SConnectByName</A></td>
206		<td><A HREF="#SSendtoByName">SSendtoByName</A></td>
207	</tr>
208	<tr>
209		<td><A HREF="#AddrToAddrStr">AddrToAddrStr</A></td>
210		<td><A HREF="#SListen">SListen</A></td>
211		<td><A HREF="#SWrite">SWrite</A></td>
212	</tr>
213	<tr>
214		<td><A HREF="#DisposeSReadlineInfo">DisposeSReadlineInfo</A></td>
215		<td><A HREF="#SNewDatagramClient">SNewDatagramClient</A></td>
216		<td><A HREF="#SelectR">SelectR</A></td>
217	</tr>
218	<tr>
219		<td><A HREF="#FlushSReadlineInfo">FlushSReadlineInfo</A></td>
220		<td><A HREF="#SNewDatagramServer">SNewDatagramServer</A></td>
221		<td><A HREF="#SelectSetAdd">SelectSetAdd</A></td>
222	</tr>
223	<tr>
224		<td><A HREF="#GetSocketBufSize">GetSocketBufSize</A></td>
225		<td><A HREF="#SNewStreamClient">SNewStreamClient</A></td>
226		<td><A HREF="#SelectSetInit">SelectSetInit</A></td>
227	</tr>
228	<tr>
229		<td><A HREF="#GetSocketLinger">GetSocketLinger</A></td>
230		<td><A HREF="#SNewStreamServer">SNewStreamServer</A></td>
231		<td><A HREF="#SelectSetRemove">SelectSetRemove</A></td>
232	</tr>
233	<tr>
234		<td><A HREF="#GetSocketNagleAlgorithm">GetSocketNagleAlgorithm</A></td>
235		<td><A HREF="#SRead">SRead</A></td>
236		<td><A HREF="#SelectW">SelectW</A></td>
237	</tr>
238	<tr>
239		<td><A HREF="#InitSReadlineInfo">InitSReadlineInfo</A></td>
240		<td><A HREF="#SReadline">SReadline</A></td>
241		<td><A HREF="#Sendto">Sendto</A></td>
242	</tr>
243	<tr>
244		<td><A HREF="#SAcceptA">SAcceptA</A></td>
245		<td><A HREF="#SRecv">SRecv</A></td>
246		<td><A HREF="#SendtoByName">SendtoByName</A></td>
247	</tr>
248	<tr>
249		<td><A HREF="#SAcceptS">SAcceptS</A></td>
250		<td><A HREF="#SRecvfrom">SRecvfrom</A></td>
251		<td><A HREF="#SetSocketBufSize">SetSocketBufSize</A></td>
252	</tr>
253	<tr>
254		<td><A HREF="#SBind">SBind</A></td>
255		<td><A HREF="#SRecvmsg">SRecvmsg</A></td>
256		<td><A HREF="#SetSocketLinger">SetSocketLinger</A></td>
257	</tr>
258	<tr>
259		<td><A HREF="#SClose">SClose</A></td>
260		<td><A HREF="#SSend">SSend</A></td>
261		<td><A HREF="#SetSocketNagleAlgorithm">SetSocketNagleAlgorithm</A></td>
262	</tr>
263	<tr>
264		<td><A HREF="#SConnect">SConnect</A></td>
265		<td><A HREF="#SSendto">SSendto</A></td>
266	</tr>
267</table>
268</UL>
269
270<H5>
271<A NAME="AddrStrToAddr"></A>AddrStrToAddr</H5>
272<TT>int</TT> <TT>AddrStrToAddr(const char * const s, struct sockaddr_in
273* const sa, const int defaultPort);</TT>
274
275<P>This takes a textual internet address specification and converts it
276to a <TT>struct sockaddr_in</TT>.&nbsp; An address string may be any of
277the following forms:
278<UL>
279<LI>
280<TT>hostname:port</TT></LI>
281
282<LI>
283<TT>service://hostname</TT></LI>
284
285<LI>
286<TT>service://hostname:port</TT></LI>
287
288<LI>
289<TT>service://hostname[/more/junk/which/is/ignored/]</TT></LI>
290
291<LI>
292<TT>port@hostname</TT></LI>
293</UL>
294Additionally, the <I>hostname</I> may be a name or an IP address string
295(i.e. <TT>192.168.1.13</TT>).
296
297<P>If the string contains a hostname but a port number does not appear
298to be present and the <I>defaultPort</I> parameter is greater than zero,
299then the address structure will use <I>defaultPort</I> as the port number.
300
301<P>The function returns a negative number if the string could not be converted,
302such as the case where the hostname was not found in the name service.
303Name service is not used unless there is a name instead of an IP address,
304so if you don't want to use name service, only use IP addresses.
305
306<P><I>Example</I>:
307<UL>
308<PRE>struct sockaddr_in addr;
309int result;
310
311result = AddrStrToAddr("ftp.probe.net", &amp;addr, 21);
312result = AddrStrToAddr("ftp.probe.net:21", &amp;addr, 21);
313result = AddrStrToAddr("206.28.166.234:21", &amp;addr, 21);
314result = AddrStrToAddr("ftp://ftp.probe.net", &amp;addr, 0);
315result = AddrStrToAddr("21@ftp.probe.net", &amp;addr, 0);</PRE>
316</UL>
317
318<UL>
319<PRE></PRE>
320</UL>
321
322<H5>
323<A NAME="#AddrToAddrStr"></A>AddrToAddrStr</H5>
324<TT>char *AddrToAddrStr(char *const dst, size_t dsize, struct sockaddr_in
325* const saddrp, int dns, const char *fmt);</TT>
326
327<P>This function takes a <TT>struct sockaddr_in</TT> and converts into
328a readable internet textual address.
329
330<P>The <I>dns</I> parameter specifies if name service should be used to
331lookup the symbolic hostname from the raw address.&nbsp; If zero, it expresses
332the raw IP address in the standard dotted-decimal format, like 192.168.1.13.
333
334<P>The <I>fmt</I> parameter is a magic cookie string, with the following
335cookies:
336<UL>
337<LI>
338<TT>%h</TT> = hostname</LI>
339
340<LI>
341<TT>%p</TT> = port number</LI>
342
343<LI>
344<TT>%s</TT> = service name (based off of the port number)</LI>
345</UL>
346This lets you print the address in just about any way you like.&nbsp; The
347<I>dst</I> parameter is the string to write the results to, and is always
348null-terminated.&nbsp; The <I>dst</I> parameter is also returned as the
349result of the function.
350
351<P><I>Example</I>:
352<UL>
353<PRE>char str[128];
354
355fputs(AddrToAddrStr(str, sizeof(str), &amp;sin, 1, "%h"), stdout);
356fputs(AddrToAddrStr(str, sizeof(str), &amp;sin, 1, "%h:%p"), stdout);
357fputs(AddrToAddrStr(str, sizeof(str), &amp;sin, 1, "%s://%h"), stdout);</PRE>
358</UL>
359
360<H5>
361<A NAME="#DisposeSReadlineInfo"></A>DisposeSReadlineInfo</H5>
362<TT>void DisposeSReadlineInfo(SReadlineInfo *srl);</TT>
363
364<P>This function is used to dispose of a <TT>SReadlineInfo</TT> structure
365that was created using <TT><A HREF="#InitSReadlineInfo">InitSReadlineInfo()</A></TT>.&nbsp;
366You're required to use this to free dynamically allocated buffers it creates
367unless you specified that you wanted to use your own buffer, in which case
368it's optional (but recommended for clarity).
369<H5>
370<A NAME="#FlushSReadlineInfo"></A>FlushSReadlineInfo</H5>
371<TT>void FlushSReadlineInfo(SReadlineInfo *srl);</TT>
372
373<P>This rarely used function is used to reset and clear the buffer used
374by <TT><A HREF="#SReadline">SReadline()</A></TT>.&nbsp; It acts similarly
375to a <TT>fflush(stdin)</TT>.
376<H5>
377<A NAME="#GetSocketBufSize"></A>GetSocketBufSize</H5>
378<TT>int GetSocketBufSize(int sockfd, size_t *const rsize, size_t *const
379ssize);</TT>
380
381<P>This utility routine returns the size of the socket's internal buffers,
382as maintained by the kernel (or socket library).&nbsp; It does this by
383calling <TT>getsockopt()</TT> with the <TT>SO_RCVBUF</TT> and <TT>SO_SNDBUF</TT>
384options, if they are defined.&nbsp; In the event they aren't defined, it
385returns a negative result code.
386
387<P><I>Example</I>:
388<UL>
389<PRE>size_t rsize, ssize;
390
391if (GetSocketBufSize(sockfd, &amp;rsize, &amp;ssize) == 0) ...</PRE>
392</UL>
393
394<H5>
395<A NAME="#GetSocketLinger"></A>GetSocketLinger</H5>
396<TT>int GetSocketLinger(const int fd, int *const lingertime);</TT>
397
398<P>This utility routine returns whether linger mode has been turned on,
399and also sets the amount of time in the <I>lingertime</I> parameter.
400
401<P><I>Example</I>:
402<UL>
403<PRE>int lingertime;
404
405if (GetSocketLinger(sockfd, &amp;lingtime) > 0)
406&nbsp;&nbsp;&nbsp; /* linger is on... */ ...;</PRE>
407</UL>
408
409<H5>
410<A NAME="#GetSocketNagleAlgorithm"></A>GetSocketNagleAlgorithm</H5>
411<TT>int GetSocketNagleAlgorithm(const int fd);</TT>
412
413<P>This utility routine returns whether the <I>Nagle Algorithm</I> is in
414effect (<TT>TCP_NODELAY</TT> mode not on).
415<H5>
416<A NAME="#InitSReadlineInfo"></A>InitSReadlineInfo</H5>
417<TT>int InitSReadlineInfo(SReadlineInfo *srl, int sockfd, char *buf, size_t
418bsize, int tlen);</TT>
419
420<P>This function is used to prepare a <TT>SReadlineInfo</TT> structure
421for use with the <TT><A HREF="#SReadline">SReadline()</A></TT> function.&nbsp;
422The <I>sockfd</I> parameter specifies the socket that will be used for
423line buffering.&nbsp; The <I>sio</I> library does not open or close this
424socket.
425
426<P>The buf parameter is the area of memory to use for buffering; it should
427be large enough to hold several lines of data.&nbsp; If <I>buf</I> is NULL,
428the function will <TT>malloc()</TT> one of <I>bsize</I> bytes, otherwise
429it is assumed that buf is maintained by you and is of size <I>bsize</I>
430bytes.&nbsp; If you let <I>sio</I> <TT>malloc()</TT> this buffer, you must
431also use <TT><A HREF="#DisposeSReadlineInfo">DisposeSReadlineInfo()</A></TT>
432to free it when you're finished with it.
433
434<P>The <I>tlen</I> parameter is the timeout value (in seconds) to use for
435each call of <TT><A HREF="#SReadline">SReadline()</A></TT>.
436
437<P><I>Example</I>:
438<UL>
439<PRE>SReadlineInfo sri;
440char localbuf[2048];
441
442if (InitSReadlineInfo(&amp;sri, sockfd, NULL, 512, 10) &lt; 0)
443&nbsp;&nbsp;&nbsp; perror("malloc 512 bytes failed");
444...or...
445(void) InitSReadlineInfo(&amp;sri, sockfd, localbuf, sizeof(localbuf), 10);</PRE>
446</UL>
447
448<H5>
449<A NAME="SAccept"></A>SAccept</H5>
450<TT>int SAccept(int sfd, struct sockaddr_in *const addr, int tlen);</TT>
451
452<P>This is does an <TT>accept()</TT>, with a timeout of <I>tlen</I> seconds
453(<I>tlen</I> may be zero to mean block indefinitely).&nbsp; If no new connection
454is accepted, <TT>kTimeoutErr</TT> is returned, otherwise a new socket or
455(-1) is returned.&nbsp; The socket is still usable if a timeout occurred,
456so you can call <TT>SAccept()</TT> again.
457<H5>
458<A NAME="#SBind"></A>SBind</H5>
459<TT>int SBind(int sockfd, const int port, const int nTries, const int reuseFlag);</TT>
460
461<P>This calls <TT>bind()</TT>, to the wildcard address with the specified
462<I>port </I>(not in network byte order).&nbsp; The <I>nTries</I> parameter
463tells how many attempts it tries before giving up (which is useful if other
464processes are grabbing the same port number).&nbsp; If the <I>reuseFlag</I>
465parameter is non-zero, <TT>SBind()</TT> tries to turn on the <TT>SO_REUSEADDR</TT>
466(and <TT>SO_REUSEPORT</TT>, if available) socket options before binding.
467
468<P>Normally you will not call this function directly, since <TT><A HREF="#SNewStreamServer">SNewStreamServer()</A></TT>
469and <TT><A HREF="#SNewDatagramServer">SNewDatagramServer()</A></TT> do
470this for you.
471<H5>
472<A NAME="#SClose"></A>SClose</H5>
473<TT>int SClose(int sfd, int tlen);</TT>
474
475<P>This is <TT>close()</TT> with a timeout of <I>tlen</I> seconds.&nbsp;
476Normally you don't need to worry about <TT>close()</TT> blocking, but if
477you have turned on linger mode, <TT>close()</TT> could block.&nbsp; <TT>SClose()</TT>
478calls <TT>close()</TT> for up to <I>tlen</I> seconds, and if the timeout
479expires, it calls <TT>shutdown()</TT> on the socket.
480<H5>
481<A NAME="#SConnect"></A>SConnect</H5>
482<TT>int SConnect(int sfd, const struct sockaddr_in *const addr, int tlen);</TT>
483
484<P>This is <TT>connect()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
485may be zero to mean block indefinitely).&nbsp; If it returns (-1) or <TT>kTimeoutErr</TT>,
486the socket is no longer usable and must be closed.
487<H5>
488<A NAME="#SConnectByName"></A>SConnectByName</H5>
489<TT>int SConnectByName(int sfd, const char * const addrStr, const int tlen);</TT>
490
491<P>This is <TT>connect()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
492may be zero to mean block indefinitely).&nbsp; If it returns (-1) or <TT>kTimeoutErr</TT>,
493the socket is no longer usable and must be closed. The difference between
494<TT><A HREF="#SConnect">SConnect()</A></TT> is that this function takes
495a textual address string instead of a <TT>struct sockaddr_in</TT>.
496
497<P><I>Example</I>:
498<UL>
499<PRE>if (SConnectByName(sockfd, "http://www.probe.net", 15) == 0) ...</PRE>
500</UL>
501
502<H5>
503<A NAME="#SListen"></A>SListen</H5>
504<TT>int SListen(int sfd, int backlog);</TT>
505
506<P>This isn't too useful at present, since it just does <TT>listen(sfd,
507backlog)</TT>.&nbsp; And, you will not call this function directly, since
508<TT><A HREF="#SNewStreamServer">SNewStreamServer()</A></TT> and <TT><A HREF="#SNewDatagramServer">SNewDatagramServer()</A></TT>
509do this for you.
510<H5>
511<A NAME="#SNewDatagramClient"></A>SNewDatagramClient</H5>
512<TT>int SNewDatagramClient(void);</TT>
513
514<P>This returns a new datagram socket, which is ready to send (and then
515receive) datagrams.&nbsp; This function is just <TT>socket(AF_INET, SOCK_DGRAM,
5160)</TT>. If successful, it returns a non-negative socket descriptor.
517
518<P><I>Example</I>:
519<UL>
520<PRE>int sockfd;
521
522sockfd = SNewDatagramClient();</PRE>
523</UL>
524
525<H5>
526<A NAME="#SNewDatagramServer"></A>SNewDatagramServer</H5>
527<TT>int SNewDatagramServer(const int port, const int nTries, const int
528reuseFlag);</TT>
529
530<P>This function creates a new socket and binds it to the specified <I>port</I>
531(not in network byte order) on the wildcard address.&nbsp; The <I>nTries</I>
532and <I>reuseFlag</I> are used when it calls <TT><A HREF="#SBind">SBind()</A></TT>.
533If successful, it returns a non-negative socket descriptor.
534
535<P><I>Example</I>:
536<UL>
537<PRE>int sockfd;
538
539sockfd = SNewDatagramServer(13, 3, 0);
540if (sockfd >= 0)
541&nbsp;&nbsp;&nbsp; /* ready to receive requests on the daytime port (13) */</PRE>
542</UL>
543
544<H5>
545<A NAME="#SNewStreamClient"></A>SNewStreamClient</H5>
546<TT>int SNewStreamClient(void);</TT>
547<P>
548This returns a new stream socket, which is ready to <TT><A HREF="#SConnect">SConnect()</A></TT>
549to a server.&nbsp; This function is just <TT>socket(AF_INET, SOCK_STREAM,
5500)</TT>.&nbsp; If successful, it returns a non-negative socket descriptor.
551
552<P><I>Example</I>:
553<UL>
554<PRE>int sockfd;
555
556sockfd = SNewStreamClient();</PRE>
557</UL>
558
559<H5>
560<A NAME="#SNewStreamServer"></A>SNewStreamServer</H5>
561<TT>int SNewStreamServer(const int port, const int nTries, const int reuseFlag,
562int listenQueueSize);</TT>
563
564<P>This function creates a new socket, binds it to the specified <I>port</I>
565(not in network byte order) on the wildcard address, and turns it on for
566listening.&nbsp; The <I>nTries</I> and <I>reuseFlag</I> are used when it
567calls <TT><A HREF="#SBind">SBind()</A></TT>. The <I>listenQueueSize</I>
568is for the <TT>listen()</TT> call.&nbsp; If successful, it returns a non-negative
569socket descriptor.
570
571<P><I>Example</I>:
572<UL>
573<PRE>int sockfd;
574
575sockfd = SNewStreamServer(80, 3, 0);
576if (sockfd >= 0)
577&nbsp;&nbsp;&nbsp; /* ready to accept HTTP connections */</PRE>
578</UL>
579
580<H5>
581<A NAME="#SRead"></A>SRead</H5>
582<TT>int SRead(int sfd, char *const buf0, size_t size, int tlen, int retry);</TT>
583
584<P>This is <TT>read()</TT> on a socket descriptor, with a timeout of <I>tlen</I>
585seconds (<I>tlen</I> must be greater than zero).&nbsp; Like <TT>read()</TT>,
586it can return 0, (-1), or the number of bytes read, but in addition, it
587can also return <TT>kTimeoutErr</TT>.
588
589<P>If <I>retry</I> is set to <TT>kFullBufferRequired</TT>, <TT>SRead()</TT>
590does not return until <I>size</I> bytes has been read or EOF is encountered.&nbsp;
591This is useful if you expect fixed-length records, and it doesn't do much
592good until a complete record has been read. However, it is still possible
593that an EOF is encountered after some bytes have been read, and with <I>retry</I>
594set to <TT>kFullBufferRequired</TT>, you get EOF instead of that partial
595record.&nbsp; If you set retry to <TT>kFullBufferRequiredExceptLast</TT>,
596you get the partial record (and EOF on the next <TT>SRead()</TT>).&nbsp;
597Otherwise, if you should set retry to kFullBufferNotRequired and SRead()
598will return when there is some data read.
599
600<P><I>Example</I>:
601<UL>
602<PRE>int nread;
603char buf[256];
604
605while (1) {
606&nbsp;&nbsp;&nbsp; nread = SRead(sockfd, buf, sizeof(buf), 15, kFullBufferNotRequired);
607&nbsp;&nbsp;&nbsp; if (nread &lt;= 0) {
608&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (nread == 0)
609&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;&nbsp;&nbsp;&nbsp; /* okay, EOF */
610&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (nread == kTimeoutErr) {
611&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "timed-out\n");
612&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;
613&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {
614&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("read");
615&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
616&nbsp;&nbsp;&nbsp; }
617&nbsp;&nbsp;&nbsp; (void) write(1, buf, nread);
618}</PRE>
619</UL>
620
621<H5>
622<A NAME="#SReadline"></A>SReadline</H5>
623<TT>int SReadline(SReadlineInfo *srl, char *const linebuf, size_t linebufsize);</TT>
624
625<P>It is often desirable to process data from sockets line by line, however
626this is cumbersome to do on a socket descriptor.&nbsp; The <TT>SReadline()</TT>
627function allows you to do this, so you can do one call of the function
628and get back a line of input.&nbsp; It accomplishes this much the same
629way the standard library's I/O routines do this, using a buffer.&nbsp;
630However, it is usually not possible to determine if the standard library
631takes the same special I/O measures on socket descriptors that sio does,
632so using the standard library function <TT>fdopen()</TT> with a socket
633descriptor may or may not work the way you want.
634
635<P><TT>SReadline()</TT> needs to maintain state information for each call,
636so a data structure is required.&nbsp; You must first call <TT><A HREF="#InitSReadlineInfo">InitSReadlineInfo()</A></TT>
637to initialize a <TT>SReadlineInfo</TT> structure.&nbsp; After that, you
638may call <TT>SReadline()</TT> repeatedly until it returns 0 to indicate
639EOF. The function returns the number of characters in the input line, including
640a newline (however, carriage return characters are omitted).&nbsp; You
641must call <TT><A HREF="#DisposeSReadlineInfo">DisposeSReadlineInfo()</A></TT>
642if you chose to let <TT><A HREF="#InitSReadlineInfo">InitSReadlineInfo()</A></TT>
643use <TT>malloc()</TT> to allocate your buffer.
644
645<P><I>Example</I>:
646<UL>
647<PRE>SReadlineInfo sri;
648char line[80];
649int nread;
650
651if (InitSReadlineInfo(&amp;sri, sockfd, NULL, 512, 10) &lt; 0) {
652&nbsp;&nbsp;&nbsp; perror("malloc");
653&nbsp;&nbsp;&nbsp; exit(1);
654}
655while (1) {
656&nbsp;&nbsp;&nbsp; nread = SReadline(&amp;sri, line, sizeof(line));
657&nbsp;&nbsp;&nbsp; if (nread &lt;= 0) {
658&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (nread == kTimeoutErr)
659&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "readline timed-out.\n");
660&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (nread &lt; 0)
661&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("readline");
662&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;
663&nbsp;&nbsp;&nbsp; }
664&nbsp;&nbsp;&nbsp; line[nread] = '\0';&nbsp;&nbsp;&nbsp; /* omit newline */
665&nbsp;&nbsp;&nbsp; fprintf(stdout, "read [%s]\n", line);
666}
667DisposeSReadlineInfo(&amp;sri);
668(void) SClose(sockfd, 3);</PRE>
669</UL>
670
671<H5>
672<A NAME="#SRecv"></A>SRecv</H5>
673<TT>int SRecv(int sfd, char *const buf0, size_t size, int fl, int tlen,
674int retry);</TT>
675
676<P>This is the corresponding wrapper for <TT>recv()</TT>, as <TT><A HREF="#SRead">SRead()</A></TT>
677is for <TT>read()</TT>.&nbsp; You will never need this function, unless
678you need the special receiving flags that <TT>recv()</TT> gives you.
679<H5>
680<A NAME="#SRecvfrom"></A>SRecvfrom</H5>
681<TT>int SRecvfrom(int sfd, char *const buf, size_t size, int fl, struct
682sockaddr_in *const fromAddr, int tlen);</TT>
683
684<P>This is <TT>recvfrom()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
685must be greater than zero).&nbsp; Like <TT>recvfrom()</TT>, it can return
6860, (-1), or the number of bytes read, but in addition, it can also return
687<TT>kTimeoutErr</TT>. Upon a timeout, the socket is still valid for additional
688I/O.
689
690<P><I>Example</I>:
691<UL>
692<PRE>int nread;
693char buf[80];
694struct sockaddr_in remoteClientAddr;
695
696nread = SRecvfrom(sockfd, buf, sizeof(buf), 0, &amp;remoteClientAddr, 15);
697if (nread &lt;= 0) {
698&nbsp;&nbsp;&nbsp; if (nread == kTimeoutErr)
699&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "recvfrom timed-out.\n");
700&nbsp;&nbsp;&nbsp; else if (nread &lt; 0)
701&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("recvfrom");
702}</PRE>
703</UL>
704
705<H5>
706<A NAME="#SRecvmsg"></A>SRecvmsg</H5>
707<TT>int SRecvmsg(int sfd, void *const msg, int fl, int tlen);</TT>
708
709<P>This is the corresponding wrapper for <TT>recvmsg()</TT>, as <TT><A HREF="#SRead">SRead()</A></TT>
710is for <TT>read()</TT>.
711<H5>
712<A NAME="#SSend"></A>SSend</H5>
713<TT>int SSend(int sfd, char *buf0, size_t size, int fl, int tlen);</TT>
714
715<P>This is the corresponding wrapper for <TT>send()</TT>, as <TT><A HREF="#SWrite">SWrite()</A></TT>
716is for <TT>write()</TT>.&nbsp; You will never need this function, unless
717you need the special receiving flags that <TT>send()</TT> gives you.
718<H5>
719<A NAME="#SSendto"></A>SSendto</H5>
720<TT>int SSendto(int sfd, const char *const buf, size_t size, int fl, const
721struct sockaddr_in *const toAddr, int tlen);</TT>
722
723<P>This is <TT>sendto()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
724must be greater than zero).&nbsp; Like <TT>sendto()</TT>, it can return
7250, (-1), or the number of bytes sent, but in addition, it can also return
726<TT>kTimeoutErr</TT>. Upon a timeout, the socket is still valid for additional
727I/O.
728
729<P>Since <TT>sendto()</TT> rarely blocks (only if the outgoing queue is
730full), you probably do not want to use a timeout or bother with its associated
731overhead; therefore <TT><A HREF="#Sendto">Sendto()</A></TT> would be a
732better choice.
733<H5>
734<A NAME="#SSendtoByName"></A>SSendtoByName</H5>
735<TT>int SSendtoByName(int sfd, const char *const buf, size_t size, int
736fl, const char *const toAddrStr, int tlen);</TT>
737
738<P>This is <TT><A HREF="#SSendto">SSendto()</A></TT>, only you can use
739a textual internet address string instead of a <TT>struct sockaddr_in</TT>.
740<H5>
741<A NAME="#SWrite"></A>SWrite</H5>
742<TT>int SWrite(int sfd, const char *const buf0, size_t size, int tlen);</TT>
743
744<P>This is <TT>write()</TT> on a network socket with a timeout of <I>tlen</I>
745seconds (<I>tlen</I> must be greater than zero).&nbsp; Like <TT>write()</TT>,
746it can return 0, (-1), or the number of bytes sent, but in addition, it
747can also return <TT>kTimeoutErr</TT>.
748<H5>
749<A NAME="SelectR"></A>SelectR</H5>
750<TT>int SelectR(SelectSetPtr ssp, SelectSetPtr resultssp);</TT>
751
752<P>This does a <TT>select()</TT> for reading with the file descriptors
753in the specified <TT>SelectSet</TT>.&nbsp; Using a <TT>SelectSet</TT> ensures
754that the first argument to select is always correct (the smallest correct
755value, for speed) and <TT>SelectR()</TT> does not destroy the original
756<TT>fd_set</TT> and <TT>timeval</TT> (it copies it to <I>resultssp</I>
757before using <TT>select()</TT>).
758
759<P><I>Example</I>:
760<UL>
761<PRE>SelectSet ss, selected;
762
763SelectSetInit(&amp;ss, 10.0);
764SelectSetAdd(&amp;ss, sockfd1);
765SelectSetAdd(&amp;ss, sockfd2);
766rc = SelectR(&amp;ss, &amp;selected);
767if ((rc > 0) &amp;&amp; (FD_ISSET(sockfd2, &amp;selected.fds))) ...</PRE>
768</UL>
769
770<H5>
771<A NAME="SelectW"></A>SelectW</H5>
772<TT>int SelectW(SelectSetPtr ssp, SelectSetPtr resultssp);</TT>
773
774<P>This does a <TT>select()</TT> for writing with the file descriptors
775in the specified <TT>SelectSet</TT>.&nbsp; Using a <TT>SelectSet</TT> ensures
776that the first argument to select is always correct (the smallest correct
777value, for speed) and <TT>SelectW()</TT> does not destroy the original
778<TT>fd_set</TT> and <TT>timeval</TT> (it copies it to <I>resultssp</I>
779before using <TT>select()</TT>).
780
781<P><I>Example</I>:
782<UL>
783<PRE>SelectSet ss, selected;
784
785SelectSetInit(&amp;ss, 10.0);
786SelectSetAdd(&amp;ss, sockfd1);
787SelectSetAdd(&amp;ss, sockfd2);
788rc = SelectW(&amp;ss, &amp;selected);
789if ((rc > 0) &amp;&amp; (FD_ISSET(sockfd2, &amp;selected.fds))) ...</PRE>
790</UL>
791
792<H5>
793<A NAME="#SelectSetAdd"></A>SelectSetAdd</H5>
794<TT>void SelectSetAdd(SelectSetPtr const ssp, const int fd);</TT>
795
796<P>This adds a descriptor to the set to select on.
797
798<P><I>Example</I>:
799<UL>
800<PRE>SelectSet ss;
801
802SelectSetInit(&amp;ss, 10.0);
803SelectSetAdd(&amp;ss, sockfd);</PRE>
804</UL>
805
806<H5>
807<A NAME="#SelectSetInit"></A>SelectSetInit</H5>
808<TT>void SelectSetInit(SelectSetPtr const ssp, const double timeout);</TT>
809
810<P>Before adding members to the <TT>SelectSet</TT> structure, it must be
811initialized with this function.&nbsp; The timeout parameter initializes
812the timeout to use for future <TT>Selects</TT>.
813<H5>
814<A NAME="#SelectSetRemove"></A>SelectSetRemove</H5>
815<TT>void SelectSetRemove(SelectSetPtr const ssp, const int fd);</TT>
816
817<P>This removes a descriptor from the set to select on.&nbsp; You will
818need to do this just before you close a descriptor that was in the set.
819<H5>
820<A NAME="#Sendto"></A>Sendto</H5>
821<TT>int Sendto(int sfd, const char *const buf, size_t size, const struct
822sockaddr_in *const toAddr);</TT>
823
824<P>This is a simple wrapper for <TT>sendto()</TT>, which only handles <TT>EINTR</TT>
825for you.&nbsp; It does not worry about <TT>SIGPIPEs</TT>.
826<H5>
827<A NAME="#SendtoByName"></A>SendtoByName</H5>
828<TT>int SendtoByName(int sfd, const char *const buf, size_t size, const
829char *const toAddrStr);</TT>
830
831<P>This is a simple wrapper for <TT>sendto()</TT>, which only handles <TT>EINTR</TT>
832for you.&nbsp; In addition, you can use a textual internet address string
833instead of a <TT>struct sockaddr_in</TT>.
834
835<P><I>Example</I>:
836<UL>
837<PRE>if (SendtoByName(sockfd, msg, sizeof(msg), "elwood.probe.net:13") > 0) ...</PRE>
838</UL>
839
840<H5>
841<A NAME="#SetSocketBufSize"></A>SetSocketBufSize</H5>
842<TT>int SetSocketBufSize(int sockfd, size_t rsize, size_t ssize);</TT>
843
844<P>This utility routine changes the size of the socket's internal buffers,
845as maintained by the kernel (or socket library).&nbsp; It does this by
846calling <TT>setsockopt()</TT> with the <TT>SO_RCVBUF</TT> and <TT>SO_SNDBUF</TT>
847options, if they are defined.&nbsp; In the event they aren't defined, it
848returns a negative result code.&nbsp; The operation is only performed if
849the size is greater than zero, so if you only wanted to change the receive
850buffer you could set <I>rsize</I> to greater than zero and <I>ssize</I>
851to 0.
852<H5>
853<A NAME="#SetSocketLinger"></A>SetSocketLinger</H5>
854<TT>int SetSocketLinger(const int fd, const int l_onoff, const int l_linger);</TT>
855
856<P>This is an interface to the <TT>SO_LINGER</TT> socket option.&nbsp;
857The <I>l_onoff</I> parameter is a boolean specifying whether linger is
858on, and the <I>l_linger</I> parameter specifies the length of the linger
859time if enabled.
860
861<P><I>Example</I>:
862<UL>
863<PRE>if (SetSocketLinger(sockfd, 1, 90) == 0) ...</PRE>
864</UL>
865
866<H5>
867<A NAME="#SetSocketNagleAlgorithm"></A>SetSocketNagleAlgorithm</H5>
868<TT>int SetSocketNagleAlgorithm(const int fd, const int onoff);</TT>
869
870<P>This utility routine enables or disables the <I>Nagle Algorithm</I>
871(<TT>TCP_NODELAY</TT> mode is off or on).&nbsp; Generally you won't care
872about this, unless you're writing an interactive application like <I>telnet</I>,
873<I>talk</I>, or <I>rlogin</I>, where response time is more important than
874throughput.
875
876<P><I>Example</I>:
877<UL>
878<PRE>if (SetSocketNagleAlgorithm(sockfd, 0) == 0) ...</PRE>
879</UL>
880
881<H3>
882<A NAME="Sample"></A>Sample Code</H3>
883Here is an example client and server that uses the <I>sio</I> library routines.&nbsp;
884The server simply takes data and uppercases any lower case data, and returns
885the result to the client.&nbsp; To try it on port number 5123, you could
886run "<TT>ucase_s 5123</TT>" in one window, and "<TT>ucase_c localhost:5123</TT>"
887in another.
888<BR>&nbsp;
889<H4>
890Server:</H4>
891
892<PRE>/* ucase_s.c */
893
894#include &lt;unistd.h&gt;
895#include &lt;sys/types.h&gt;
896#include &lt;sys/socket.h&gt;
897#include &lt;sys/wait.h&gt;
898#include &lt;netinet/in.h&gt;
899#include &lt;arpa/inet.h&gt;
900#include &lt;errno.h&gt;
901#include &lt;stdio.h&gt;
902#include &lt;string.h&gt;
903#include &lt;stdlib.h&gt;
904#include &lt;time.h&gt;
905
906#include &quot;sio.h&quot;
907
908static void
909ServeOneClient(int sockfd, struct sockaddr_in *cliAddr)
910{
911	char buf[32], cliAddrStr[64];
912	int nread, nwrote, i;
913
914	printf(&quot;subserver[%d]: started, connected to %s.\n&quot;, (int) getpid(),
915		AddrToAddrStr(cliAddrStr, sizeof(cliAddrStr), cliAddr, 1, &quot;&lt;%h:%p&gt;&quot;)
916	);
917	for (;;) {
918		nread = SRead(sockfd, buf, sizeof(buf), 15, kFullBufferNotRequired);
919		if (nread == 0) {
920			break;
921		} else if (nread &lt; 0) {
922			fprintf(stderr, &quot;subserver[%d]: read error: %s\n&quot;,
923				(int) getpid(), strerror(errno));
924			break;
925		}
926		for (i=0; i&lt;nread; i++)
927			if (islower(buf[i]))
928				buf[i] = toupper(buf[i]);
929		nwrote = SWrite(sockfd, buf, nread, 15);
930		if (nwrote &lt; 0) {
931			fprintf(stderr, &quot;subserver[%d]: write error: %s\n&quot;,
932				(int) getpid(), strerror(errno));
933			break;
934		}
935	}
936	(void) SClose(sockfd, 10);
937	printf(&quot;subserver[%d]: done.\n&quot;, (int) getpid());
938	exit(0);
939}	/* ServeOneClient */
940
941
942static void
943Server(int port)
944{
945	int sockfd, newsockfd;
946	struct sockaddr_in cliAddr;
947	int pid;
948
949	sockfd = SNewStreamServer(port, 3, kReUseAddrYes, 3);
950	if (sockfd &lt; 0) {
951		perror(&quot;Server setup failed&quot;);
952		exit(1);
953	}
954
955	printf(&quot;server[%d]: started.\n&quot;, (int) getpid());
956	for(;;) {
957		while (waitpid(-1, NULL, WNOHANG) &gt; 0) ;
958		newsockfd = SAccept(sockfd, &amp;cliAddr, 5);
959		if (newsockfd &lt; 0) {
960			if (newsockfd == kTimeoutErr)
961				printf(&quot;server[%d]: idle\n&quot;, (int) getpid());
962			else
963				fprintf(stderr, &quot;server[%d]: accept error: %s\n&quot;,
964					(int) getpid(), strerror(errno));
965		} else if ((pid = fork()) &lt; 0) {
966			fprintf(stderr, &quot;server[%d]: fork error: %s\n&quot;,
967				(int) getpid(), strerror(errno));
968			exit(1);
969		} else if (pid == 0) {
970			ServeOneClient(newsockfd, &amp;cliAddr);
971			exit(0);
972		} else {
973			/* Parent doesn't need it now. */
974			(void) close(newsockfd);
975		}
976	}
977}	/* Server */
978
979
980void
981main(int argc, char **argv)
982{
983	int port;
984
985	if (argc &lt; 2) {
986		fprintf(stderr, &quot;Usage: %s &lt;port&gt;\n&quot;, argv[0]);
987		exit(2);
988	}
989	port = atoi(argv[1]);
990	Server(port);
991	exit(0);
992}	/* main */
993</PRE>
994
995<H4>
996Client:</H4>
997
998<PRE>/* ucase_c.c */
999
1000#include &lt;unistd.h&gt;
1001#include &lt;sys/types.h&gt;
1002#include &lt;sys/socket.h&gt;
1003#include &lt;sys/wait.h&gt;
1004#include &lt;netinet/in.h&gt;
1005#include &lt;arpa/inet.h&gt;
1006#include &lt;errno.h&gt;
1007#include &lt;stdio.h&gt;
1008#include &lt;string.h&gt;
1009#include &lt;stdlib.h&gt;
1010#include &lt;time.h&gt;
1011
1012#include &quot;sio.h&quot;
1013
1014static void
1015Client(char *serverAddrStr)
1016{
1017	char buf[256];
1018	int nread, nwrote, sockfd;
1019
1020	sockfd = SNewStreamClient();
1021	if (sockfd &lt; 0) {
1022		fprintf(stderr, &quot;client[%d]: socket error: %s\n&quot;,
1023			(int) getpid(), strerror(errno));
1024		exit(1);
1025	}
1026
1027	if (SConnectByName(sockfd, serverAddrStr, 15) &lt; 0) {
1028		fprintf(stderr, &quot;client[%d]: could not connect to &lt;%s&gt;: %s\n&quot;,
1029			(int) getpid(), serverAddrStr, strerror(errno));
1030		exit(1);
1031	}
1032
1033	printf(&quot;client[%d]: connected to &lt;%s&gt;.\n&quot;, (int) getpid(), serverAddrStr);
1034	for (buf[sizeof(buf) - 1] = '\0';;) {
1035		printf(&quot;client[%d]: Enter message to send -&gt; &quot;, (int) getpid());
1036		if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
1037			break;
1038		buf[strlen(buf) - 1] = '\0';	/* Delete newline. */
1039		if (buf[0] == '\0')
1040			continue;		/* Blank line. */
1041
1042		/* Send the request line to the server. */
1043		nwrote = SWrite(sockfd, buf, strlen(buf), 15);
1044		if (nwrote &lt; 0) {
1045			fprintf(stderr, &quot;client[%d]: write error: %s\n&quot;,
1046				(int) getpid(), strerror(errno));
1047			break;
1048		}
1049
1050		/* Wait for complete reply line */
1051		nread = SRead(sockfd, buf, nwrote, 15, kFullBufferRequired);
1052		if (nread == 0) {
1053			fprintf(stderr, &quot;client[%d]: no reply received (EOF).\n&quot;,
1054				(int) getpid());
1055			break;
1056		} else if (nread &lt; 0) {
1057			fprintf(stderr, &quot;client[%d]: read error: %s\n&quot;,
1058				(int) getpid(), strerror(errno));
1059			break;
1060		}
1061		buf[nread] = '\0';
1062		fprintf(stdout, &quot;client[%d]: received: %s\n&quot;,
1063			(int) getpid(), buf);
1064	}
1065	(void) SClose(sockfd, 10);
1066	printf(&quot;\nclient[%d]: done.\n&quot;, (int) getpid());
1067	exit(0);
1068}	/* Client */
1069
1070
1071void
1072main(int argc, char **argv)
1073{
1074	int port;
1075
1076	if (argc &lt; 2) {
1077		fprintf(stderr, &quot;Usage: %s &lt;host:port&gt;\n&quot;, argv[0]);
1078		exit(2);
1079	}
1080	Client(argv[1]);
1081	exit(0);
1082}	/* main */
1083</PRE>
1084
1085</BODY>
1086</HTML>
1087