mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 20:32:36 +00:00
1087 lines
39 KiB
HTML
1087 lines
39 KiB
HTML
<HTML>
|
|
<HEAD>
|
|
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
|
|
<META NAME="Author" CONTENT="Mike Gleason">
|
|
<META NAME="GENERATOR" CONTENT="Mozilla/4.03 [en] (WinNT; I) [Netscape]">
|
|
<TITLE>sio: SocketIO Library</TITLE>
|
|
</HEAD>
|
|
<BODY BGCOLOR="#FFFFFF">
|
|
|
|
<H1>
|
|
SocketIO Library Documentation</H1>
|
|
|
|
<UL>
|
|
<LI>
|
|
<A HREF="#Introduction">Introduction</A></LI>
|
|
|
|
<LI>
|
|
<A HREF="#Installation">Installation</A></LI>
|
|
|
|
<LI>
|
|
<A HREF="#Functions">Function Reference</A></LI>
|
|
|
|
<LI>
|
|
<A HREF="#Sample">Sample Code</A></LI>
|
|
</UL>
|
|
The purpose of the <I>SocketIO Library</I> (<I>sio</I> for short) is to
|
|
provide a safe and consistent wrapper interface to a BSD Sockets implementation.
|
|
The library also takes steps to protect against a few common pitfalls that
|
|
plague poorly written BSD Sockets programs. Specifically, it was
|
|
designed to do the following:
|
|
|
|
|
|
<UL>
|
|
<LI>
|
|
Take care of reading and writing the full operation.
|
|
|
|
<P><FONT SIZE=-1>For example, a network <TT>write()</TT> for 50 bytes may
|
|
only write 30 bytes; <I>Sio</I> would continue writing until all 50 bytes
|
|
have been sent.</FONT>
|
|
<P></LI>
|
|
|
|
<LI>
|
|
<P>
|
|
Allow operations that would normally block can be set to timeout after
|
|
a customizable period of time.<P></LI>
|
|
|
|
<LI>
|
|
<P>
|
|
Catch the SIGPIPE signal which would cause an unexpecting process to exit.<P>
|
|
|
|
<P><FONT SIZE=-1>A frequent source of problems for beginning programmers
|
|
is the situation where their process is suddenly no longer running because
|
|
a write to a socket caused a broken pipe. This problem can be difficult
|
|
to diagnose if the process is running as a daemon process in the background.</FONT>
|
|
<P>
|
|
</LI>
|
|
|
|
<LI>
|
|
<P>
|
|
Resume operation when system calls are interrupted (operations errout
|
|
with <TT>EINTR</TT>).<P></LI>
|
|
|
|
<LI>
|
|
<P>
|
|
Cater to the internet socket interface (i.e. <TT>struct sockaddr_in</TT>).<P></LI>
|
|
|
|
<LI>
|
|
<P>
|
|
Simplified interface to name service.
|
|
|
|
|
|
<P><FONT SIZE=-1>Library routines can take a textual address specification
|
|
instead of you having to prepare a <TT>struct sockaddr_in</TT>.</FONT>
|
|
<P>
|
|
</LI>
|
|
|
|
</UL>
|
|
The library was written by Mike Gleason. The first incarnation dates
|
|
back to some time in 1992. The library may be used and distributed
|
|
freely, as long as you give due credit.
|
|
<P>
|
|
<H3>
|
|
<A NAME="Introduction"></A>Introduction</H3>
|
|
The reader is assumed to be familiar with BSD Sockets. An excellent
|
|
source of information is W. Richard Stevens' <U>UNIX Network Programming,
|
|
Volume 1, Second Edition: Networking APIs: Sockets and XTI</U>, Prentice
|
|
Hall, 1998, ISBN 0-13-490012-X.
|
|
<BR>
|
|
<H4>
|
|
<A NAME="Connection_modes"></A>Connection modes</H4>
|
|
A communications exchange between two end-points can be connection-oriented
|
|
or connectionless. An connection-oriented exchange is characterized
|
|
by a connection establishment, a series of messages, and a disconnection.
|
|
A connectionless exchange is characterized by one or more independent messages.
|
|
An analogy to a connection-oriented exchange would be a telephone call
|
|
where one party establishes a connection to another, and a conversation
|
|
ensues. A connectionless exchange analogy would be a letter sent
|
|
via the postal service -- even if a writer sends two letters to the same
|
|
recipient, each letter is transported independently.
|
|
<BR>
|
|
<H4>
|
|
<A NAME="Message_modes"></A>Message modes</H4>
|
|
For the <I>sio</I> library, all connection-oriented exchanges are data
|
|
streams using the <I>TCP/IP</I> protocol. After connection establishment,
|
|
the conversation consists of one long sequence of data bytes, which is
|
|
known as a <I>stream</I>. Therefore there is no concept of a record
|
|
boundary associated with a stream connection at the transport level, although
|
|
often there is a record associated over the stream at the application level.
|
|
The important thing here is to realize that there is no flow control associated
|
|
with the stream, so although the data bytes are guaranteed to arrive in
|
|
order, there is no guarantee that messages will be read in the same size
|
|
blocks as they were originally written.
|
|
|
|
<P>For connectionless exchanges, <I>sio</I> uses the <I>UDP/IP</I> protocol's
|
|
datagram messages. There is an implicit definition of a record boundary,
|
|
because each message is treated as a record. So, three writes results in
|
|
three separate messages.
|
|
|
|
<P>For example, let's say a sender writes three 50-byte messages.
|
|
If a receiver does a 20-byte read followed by 100-byte read followed by
|
|
a 50-byte read, the receiver would get the first 20 bytes of the first
|
|
message, 50 of 50 bytes of the second message, and 50 of 50 bytes of the
|
|
third message. It's important to understand that in the first read
|
|
that since UDP datagrams are message oriented that the remaining 30 bytes
|
|
of the first message are lost, and that although the second read is for
|
|
100 bytes, the read immediately returns as finished even though only 50
|
|
bytes were actually read.
|
|
|
|
<P>With UDP datagrams, there are also other important implications that
|
|
affect message ordering, duplication, and overall reliability.
|
|
<BR>
|
|
<H4>
|
|
<A NAME="Creating_and_disposing"></A>Creating and disposing sockets</H4>
|
|
A socket is an I/O descriptor that is associated with one side of a communications
|
|
exchange. A socket must be either a client or a server, although
|
|
after communications is established the difference is arbitrary.
|
|
A server socket is one that waits for contact to be initiated by a client
|
|
on a mutually agreed upon address and port number. A client socket
|
|
initiates the first communication by sending to an existing server socket.
|
|
Client and server sockets may of course be on different machines, and different
|
|
networks altogether.
|
|
|
|
<P>A stream server socket is created using <TT><A HREF="#SNewStreamServer">SNewStreamServer()</A></TT>,
|
|
which returns a socket file descriptor ready to accept new client connections.
|
|
After a socket descriptor is obtained, your server program can then use
|
|
<TT><A HREF="#SAccept">SAccept()</A></TT> to establish a connection with
|
|
a client.
|
|
|
|
<P>A stream client socket is created using <TT><A HREF="#SNewStreamClient">SNewStreamClient()</A></TT>.
|
|
After a socket descriptor is obtained, your client program can use <TT><A HREF="#SConnectByName">SConnectByName()</A></TT>
|
|
or <TT><A HREF="#SConnect">SConnect()</A></TT> to initiate a connection
|
|
to a server.
|
|
|
|
<P>A datagram server socket is created using <TT><A HREF="#SNewDatagramServer">SNewDatagramServer()</A></TT>.
|
|
After a socket descriptor is obtained, it is ready to receive messages
|
|
from clients using <TT><A HREF="#SRecvfrom">SRecvfrom()</A></TT> and reply
|
|
back with <TT><A HREF="#SSendto">SSendto()</A></TT>.
|
|
|
|
<P>A datagram client socket is created using <TT><A HREF="#SNewDatagramClient">SNewDatagramClient()</A></TT>.
|
|
After a socket descriptor is obtained, it is ready to communicate with
|
|
servers using <TT><A HREF="#SSendto">SSendto()</A></TT> and <TT><A HREF="#SRecvfrom">SRecvfrom()</A></TT>.
|
|
|
|
<P>All socket descriptors are disposed of with <TT><A HREF="#SClose">SClose()</A></TT>
|
|
when communication is finished.
|
|
<BR>
|
|
<H4>
|
|
<A NAME="SocketIO"></A>Socket I/O</H4>
|
|
A stream connection uses <TT><A HREF="#SRead">SRead()</A></TT> to receive
|
|
data from the stream and <TT><A HREF="#SWrite">SWrite()</A></TT> to send
|
|
data. A datagram socket should use <TT><A HREF="#SRecvfrom">SRecvfrom()</A></TT>
|
|
to read a message and <TT><A HREF="#SSendtoByName">SSendtoByName()</A></TT>
|
|
or <TT><A HREF="#SSendto">SSendto()</A></TT> to send a message. Since
|
|
each datagram communication is independent, these routines use an address
|
|
specifier along with each call.
|
|
<BR>
|
|
<H3>
|
|
<A NAME="Installation"></A>Installation</H3>
|
|
First, unpack the archive. If the package arrived as a '.tar.gz' or '.tgz'
|
|
file, you need to run gunzip, and then tar to extract the source files.
|
|
You can do that in one shot by doing "<TT>gunzip -c sio.tgz | tar xvf -</TT>".
|
|
If the package you have is a '.tar' file you can use "<TT>tar xvf sio.tar</TT>".
|
|
If the package you have is a '.zip' file you can use "<TT>unzip sio.zip</TT>"
|
|
or "<TT>pkunzip sio.zip</TT>".
|
|
|
|
<P>Now go to the "<TT>sio</TT>" directory you just made. There is a script
|
|
you must run which will checks your system for certain features, so that
|
|
the library can be compiled on a variety of U<FONT SIZE=-1>NIX</FONT> systems.
|
|
Run this script by typing "<TT>sh ./configure</TT>" in that directory.
|
|
After that, you can look at the <TT>Makefile</TT> it made if you like,
|
|
and then you run "<TT>make</TT>" to create the "<TT>libsio.a</TT>" library
|
|
file.
|
|
|
|
<P>Finally, install the library and headers. You can manually copy the
|
|
files, or you can run "<TT>make install</TT>" to copy the files for you.
|
|
If you choose to "<TT>make install</TT>" you may want to edit the <TT>Makefile</TT>
|
|
if you do not want to install to the <TT>/usr/local</TT> tree.
|
|
<BR>
|
|
<H3>
|
|
<A NAME="Functions"></A>Function Reference</H3>
|
|
|
|
<UL>
|
|
<table width="100%">
|
|
<tr>
|
|
<td><A HREF="#AddrStrToAddr">AddrStrToAddr</A></td>
|
|
<td><A HREF="#SConnectByName">SConnectByName</A></td>
|
|
<td><A HREF="#SSendtoByName">SSendtoByName</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#AddrToAddrStr">AddrToAddrStr</A></td>
|
|
<td><A HREF="#SListen">SListen</A></td>
|
|
<td><A HREF="#SWrite">SWrite</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#DisposeSReadlineInfo">DisposeSReadlineInfo</A></td>
|
|
<td><A HREF="#SNewDatagramClient">SNewDatagramClient</A></td>
|
|
<td><A HREF="#SelectR">SelectR</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#FlushSReadlineInfo">FlushSReadlineInfo</A></td>
|
|
<td><A HREF="#SNewDatagramServer">SNewDatagramServer</A></td>
|
|
<td><A HREF="#SelectSetAdd">SelectSetAdd</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#GetSocketBufSize">GetSocketBufSize</A></td>
|
|
<td><A HREF="#SNewStreamClient">SNewStreamClient</A></td>
|
|
<td><A HREF="#SelectSetInit">SelectSetInit</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#GetSocketLinger">GetSocketLinger</A></td>
|
|
<td><A HREF="#SNewStreamServer">SNewStreamServer</A></td>
|
|
<td><A HREF="#SelectSetRemove">SelectSetRemove</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#GetSocketNagleAlgorithm">GetSocketNagleAlgorithm</A></td>
|
|
<td><A HREF="#SRead">SRead</A></td>
|
|
<td><A HREF="#SelectW">SelectW</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#InitSReadlineInfo">InitSReadlineInfo</A></td>
|
|
<td><A HREF="#SReadline">SReadline</A></td>
|
|
<td><A HREF="#Sendto">Sendto</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#SAcceptA">SAcceptA</A></td>
|
|
<td><A HREF="#SRecv">SRecv</A></td>
|
|
<td><A HREF="#SendtoByName">SendtoByName</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#SAcceptS">SAcceptS</A></td>
|
|
<td><A HREF="#SRecvfrom">SRecvfrom</A></td>
|
|
<td><A HREF="#SetSocketBufSize">SetSocketBufSize</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#SBind">SBind</A></td>
|
|
<td><A HREF="#SRecvmsg">SRecvmsg</A></td>
|
|
<td><A HREF="#SetSocketLinger">SetSocketLinger</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#SClose">SClose</A></td>
|
|
<td><A HREF="#SSend">SSend</A></td>
|
|
<td><A HREF="#SetSocketNagleAlgorithm">SetSocketNagleAlgorithm</A></td>
|
|
</tr>
|
|
<tr>
|
|
<td><A HREF="#SConnect">SConnect</A></td>
|
|
<td><A HREF="#SSendto">SSendto</A></td>
|
|
</tr>
|
|
</table>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="AddrStrToAddr"></A>AddrStrToAddr</H5>
|
|
<TT>int</TT> <TT>AddrStrToAddr(const char * const s, struct sockaddr_in
|
|
* const sa, const int defaultPort);</TT>
|
|
|
|
<P>This takes a textual internet address specification and converts it
|
|
to a <TT>struct sockaddr_in</TT>. An address string may be any of
|
|
the following forms:
|
|
<UL>
|
|
<LI>
|
|
<TT>hostname:port</TT></LI>
|
|
|
|
<LI>
|
|
<TT>service://hostname</TT></LI>
|
|
|
|
<LI>
|
|
<TT>service://hostname:port</TT></LI>
|
|
|
|
<LI>
|
|
<TT>service://hostname[/more/junk/which/is/ignored/]</TT></LI>
|
|
|
|
<LI>
|
|
<TT>port@hostname</TT></LI>
|
|
</UL>
|
|
Additionally, the <I>hostname</I> may be a name or an IP address string
|
|
(i.e. <TT>192.168.1.13</TT>).
|
|
|
|
<P>If the string contains a hostname but a port number does not appear
|
|
to be present and the <I>defaultPort</I> parameter is greater than zero,
|
|
then the address structure will use <I>defaultPort</I> as the port number.
|
|
|
|
<P>The function returns a negative number if the string could not be converted,
|
|
such as the case where the hostname was not found in the name service.
|
|
Name service is not used unless there is a name instead of an IP address,
|
|
so if you don't want to use name service, only use IP addresses.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>struct sockaddr_in addr;
|
|
int result;
|
|
|
|
result = AddrStrToAddr("ftp.probe.net", &addr, 21);
|
|
result = AddrStrToAddr("ftp.probe.net:21", &addr, 21);
|
|
result = AddrStrToAddr("206.28.166.234:21", &addr, 21);
|
|
result = AddrStrToAddr("ftp://ftp.probe.net", &addr, 0);
|
|
result = AddrStrToAddr("21@ftp.probe.net", &addr, 0);</PRE>
|
|
</UL>
|
|
|
|
<UL>
|
|
<PRE></PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#AddrToAddrStr"></A>AddrToAddrStr</H5>
|
|
<TT>char *AddrToAddrStr(char *const dst, size_t dsize, struct sockaddr_in
|
|
* const saddrp, int dns, const char *fmt);</TT>
|
|
|
|
<P>This function takes a <TT>struct sockaddr_in</TT> and converts into
|
|
a readable internet textual address.
|
|
|
|
<P>The <I>dns</I> parameter specifies if name service should be used to
|
|
lookup the symbolic hostname from the raw address. If zero, it expresses
|
|
the raw IP address in the standard dotted-decimal format, like 192.168.1.13.
|
|
|
|
<P>The <I>fmt</I> parameter is a magic cookie string, with the following
|
|
cookies:
|
|
<UL>
|
|
<LI>
|
|
<TT>%h</TT> = hostname</LI>
|
|
|
|
<LI>
|
|
<TT>%p</TT> = port number</LI>
|
|
|
|
<LI>
|
|
<TT>%s</TT> = service name (based off of the port number)</LI>
|
|
</UL>
|
|
This lets you print the address in just about any way you like. The
|
|
<I>dst</I> parameter is the string to write the results to, and is always
|
|
null-terminated. The <I>dst</I> parameter is also returned as the
|
|
result of the function.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>char str[128];
|
|
|
|
fputs(AddrToAddrStr(str, sizeof(str), &sin, 1, "%h"), stdout);
|
|
fputs(AddrToAddrStr(str, sizeof(str), &sin, 1, "%h:%p"), stdout);
|
|
fputs(AddrToAddrStr(str, sizeof(str), &sin, 1, "%s://%h"), stdout);</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#DisposeSReadlineInfo"></A>DisposeSReadlineInfo</H5>
|
|
<TT>void DisposeSReadlineInfo(SReadlineInfo *srl);</TT>
|
|
|
|
<P>This function is used to dispose of a <TT>SReadlineInfo</TT> structure
|
|
that was created using <TT><A HREF="#InitSReadlineInfo">InitSReadlineInfo()</A></TT>.
|
|
You're required to use this to free dynamically allocated buffers it creates
|
|
unless you specified that you wanted to use your own buffer, in which case
|
|
it's optional (but recommended for clarity).
|
|
<H5>
|
|
<A NAME="#FlushSReadlineInfo"></A>FlushSReadlineInfo</H5>
|
|
<TT>void FlushSReadlineInfo(SReadlineInfo *srl);</TT>
|
|
|
|
<P>This rarely used function is used to reset and clear the buffer used
|
|
by <TT><A HREF="#SReadline">SReadline()</A></TT>. It acts similarly
|
|
to a <TT>fflush(stdin)</TT>.
|
|
<H5>
|
|
<A NAME="#GetSocketBufSize"></A>GetSocketBufSize</H5>
|
|
<TT>int GetSocketBufSize(int sockfd, size_t *const rsize, size_t *const
|
|
ssize);</TT>
|
|
|
|
<P>This utility routine returns the size of the socket's internal buffers,
|
|
as maintained by the kernel (or socket library). It does this by
|
|
calling <TT>getsockopt()</TT> with the <TT>SO_RCVBUF</TT> and <TT>SO_SNDBUF</TT>
|
|
options, if they are defined. In the event they aren't defined, it
|
|
returns a negative result code.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>size_t rsize, ssize;
|
|
|
|
if (GetSocketBufSize(sockfd, &rsize, &ssize) == 0) ...</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#GetSocketLinger"></A>GetSocketLinger</H5>
|
|
<TT>int GetSocketLinger(const int fd, int *const lingertime);</TT>
|
|
|
|
<P>This utility routine returns whether linger mode has been turned on,
|
|
and also sets the amount of time in the <I>lingertime</I> parameter.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>int lingertime;
|
|
|
|
if (GetSocketLinger(sockfd, &lingtime) > 0)
|
|
/* linger is on... */ ...;</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#GetSocketNagleAlgorithm"></A>GetSocketNagleAlgorithm</H5>
|
|
<TT>int GetSocketNagleAlgorithm(const int fd);</TT>
|
|
|
|
<P>This utility routine returns whether the <I>Nagle Algorithm</I> is in
|
|
effect (<TT>TCP_NODELAY</TT> mode not on).
|
|
<H5>
|
|
<A NAME="#InitSReadlineInfo"></A>InitSReadlineInfo</H5>
|
|
<TT>int InitSReadlineInfo(SReadlineInfo *srl, int sockfd, char *buf, size_t
|
|
bsize, int tlen);</TT>
|
|
|
|
<P>This function is used to prepare a <TT>SReadlineInfo</TT> structure
|
|
for use with the <TT><A HREF="#SReadline">SReadline()</A></TT> function.
|
|
The <I>sockfd</I> parameter specifies the socket that will be used for
|
|
line buffering. The <I>sio</I> library does not open or close this
|
|
socket.
|
|
|
|
<P>The buf parameter is the area of memory to use for buffering; it should
|
|
be large enough to hold several lines of data. If <I>buf</I> is NULL,
|
|
the function will <TT>malloc()</TT> one of <I>bsize</I> bytes, otherwise
|
|
it is assumed that buf is maintained by you and is of size <I>bsize</I>
|
|
bytes. If you let <I>sio</I> <TT>malloc()</TT> this buffer, you must
|
|
also use <TT><A HREF="#DisposeSReadlineInfo">DisposeSReadlineInfo()</A></TT>
|
|
to free it when you're finished with it.
|
|
|
|
<P>The <I>tlen</I> parameter is the timeout value (in seconds) to use for
|
|
each call of <TT><A HREF="#SReadline">SReadline()</A></TT>.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>SReadlineInfo sri;
|
|
char localbuf[2048];
|
|
|
|
if (InitSReadlineInfo(&sri, sockfd, NULL, 512, 10) < 0)
|
|
perror("malloc 512 bytes failed");
|
|
...or...
|
|
(void) InitSReadlineInfo(&sri, sockfd, localbuf, sizeof(localbuf), 10);</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="SAccept"></A>SAccept</H5>
|
|
<TT>int SAccept(int sfd, struct sockaddr_in *const addr, int tlen);</TT>
|
|
|
|
<P>This is does an <TT>accept()</TT>, with a timeout of <I>tlen</I> seconds
|
|
(<I>tlen</I> may be zero to mean block indefinitely). If no new connection
|
|
is accepted, <TT>kTimeoutErr</TT> is returned, otherwise a new socket or
|
|
(-1) is returned. The socket is still usable if a timeout occurred,
|
|
so you can call <TT>SAccept()</TT> again.
|
|
<H5>
|
|
<A NAME="#SBind"></A>SBind</H5>
|
|
<TT>int SBind(int sockfd, const int port, const int nTries, const int reuseFlag);</TT>
|
|
|
|
<P>This calls <TT>bind()</TT>, to the wildcard address with the specified
|
|
<I>port </I>(not in network byte order). The <I>nTries</I> parameter
|
|
tells how many attempts it tries before giving up (which is useful if other
|
|
processes are grabbing the same port number). If the <I>reuseFlag</I>
|
|
parameter is non-zero, <TT>SBind()</TT> tries to turn on the <TT>SO_REUSEADDR</TT>
|
|
(and <TT>SO_REUSEPORT</TT>, if available) socket options before binding.
|
|
|
|
<P>Normally you will not call this function directly, since <TT><A HREF="#SNewStreamServer">SNewStreamServer()</A></TT>
|
|
and <TT><A HREF="#SNewDatagramServer">SNewDatagramServer()</A></TT> do
|
|
this for you.
|
|
<H5>
|
|
<A NAME="#SClose"></A>SClose</H5>
|
|
<TT>int SClose(int sfd, int tlen);</TT>
|
|
|
|
<P>This is <TT>close()</TT> with a timeout of <I>tlen</I> seconds.
|
|
Normally you don't need to worry about <TT>close()</TT> blocking, but if
|
|
you have turned on linger mode, <TT>close()</TT> could block. <TT>SClose()</TT>
|
|
calls <TT>close()</TT> for up to <I>tlen</I> seconds, and if the timeout
|
|
expires, it calls <TT>shutdown()</TT> on the socket.
|
|
<H5>
|
|
<A NAME="#SConnect"></A>SConnect</H5>
|
|
<TT>int SConnect(int sfd, const struct sockaddr_in *const addr, int tlen);</TT>
|
|
|
|
<P>This is <TT>connect()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
|
|
may be zero to mean block indefinitely). If it returns (-1) or <TT>kTimeoutErr</TT>,
|
|
the socket is no longer usable and must be closed.
|
|
<H5>
|
|
<A NAME="#SConnectByName"></A>SConnectByName</H5>
|
|
<TT>int SConnectByName(int sfd, const char * const addrStr, const int tlen);</TT>
|
|
|
|
<P>This is <TT>connect()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
|
|
may be zero to mean block indefinitely). If it returns (-1) or <TT>kTimeoutErr</TT>,
|
|
the socket is no longer usable and must be closed. The difference between
|
|
<TT><A HREF="#SConnect">SConnect()</A></TT> is that this function takes
|
|
a textual address string instead of a <TT>struct sockaddr_in</TT>.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>if (SConnectByName(sockfd, "http://www.probe.net", 15) == 0) ...</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SListen"></A>SListen</H5>
|
|
<TT>int SListen(int sfd, int backlog);</TT>
|
|
|
|
<P>This isn't too useful at present, since it just does <TT>listen(sfd,
|
|
backlog)</TT>. And, you will not call this function directly, since
|
|
<TT><A HREF="#SNewStreamServer">SNewStreamServer()</A></TT> and <TT><A HREF="#SNewDatagramServer">SNewDatagramServer()</A></TT>
|
|
do this for you.
|
|
<H5>
|
|
<A NAME="#SNewDatagramClient"></A>SNewDatagramClient</H5>
|
|
<TT>int SNewDatagramClient(void);</TT>
|
|
|
|
<P>This returns a new datagram socket, which is ready to send (and then
|
|
receive) datagrams. This function is just <TT>socket(AF_INET, SOCK_DGRAM,
|
|
0)</TT>. If successful, it returns a non-negative socket descriptor.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>int sockfd;
|
|
|
|
sockfd = SNewDatagramClient();</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SNewDatagramServer"></A>SNewDatagramServer</H5>
|
|
<TT>int SNewDatagramServer(const int port, const int nTries, const int
|
|
reuseFlag);</TT>
|
|
|
|
<P>This function creates a new socket and binds it to the specified <I>port</I>
|
|
(not in network byte order) on the wildcard address. The <I>nTries</I>
|
|
and <I>reuseFlag</I> are used when it calls <TT><A HREF="#SBind">SBind()</A></TT>.
|
|
If successful, it returns a non-negative socket descriptor.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>int sockfd;
|
|
|
|
sockfd = SNewDatagramServer(13, 3, 0);
|
|
if (sockfd >= 0)
|
|
/* ready to receive requests on the daytime port (13) */</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SNewStreamClient"></A>SNewStreamClient</H5>
|
|
<TT>int SNewStreamClient(void);</TT>
|
|
<P>
|
|
This returns a new stream socket, which is ready to <TT><A HREF="#SConnect">SConnect()</A></TT>
|
|
to a server. This function is just <TT>socket(AF_INET, SOCK_STREAM,
|
|
0)</TT>. If successful, it returns a non-negative socket descriptor.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>int sockfd;
|
|
|
|
sockfd = SNewStreamClient();</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SNewStreamServer"></A>SNewStreamServer</H5>
|
|
<TT>int SNewStreamServer(const int port, const int nTries, const int reuseFlag,
|
|
int listenQueueSize);</TT>
|
|
|
|
<P>This function creates a new socket, binds it to the specified <I>port</I>
|
|
(not in network byte order) on the wildcard address, and turns it on for
|
|
listening. The <I>nTries</I> and <I>reuseFlag</I> are used when it
|
|
calls <TT><A HREF="#SBind">SBind()</A></TT>. The <I>listenQueueSize</I>
|
|
is for the <TT>listen()</TT> call. If successful, it returns a non-negative
|
|
socket descriptor.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>int sockfd;
|
|
|
|
sockfd = SNewStreamServer(80, 3, 0);
|
|
if (sockfd >= 0)
|
|
/* ready to accept HTTP connections */</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SRead"></A>SRead</H5>
|
|
<TT>int SRead(int sfd, char *const buf0, size_t size, int tlen, int retry);</TT>
|
|
|
|
<P>This is <TT>read()</TT> on a socket descriptor, with a timeout of <I>tlen</I>
|
|
seconds (<I>tlen</I> must be greater than zero). Like <TT>read()</TT>,
|
|
it can return 0, (-1), or the number of bytes read, but in addition, it
|
|
can also return <TT>kTimeoutErr</TT>.
|
|
|
|
<P>If <I>retry</I> is set to <TT>kFullBufferRequired</TT>, <TT>SRead()</TT>
|
|
does not return until <I>size</I> bytes has been read or EOF is encountered.
|
|
This is useful if you expect fixed-length records, and it doesn't do much
|
|
good until a complete record has been read. However, it is still possible
|
|
that an EOF is encountered after some bytes have been read, and with <I>retry</I>
|
|
set to <TT>kFullBufferRequired</TT>, you get EOF instead of that partial
|
|
record. If you set retry to <TT>kFullBufferRequiredExceptLast</TT>,
|
|
you get the partial record (and EOF on the next <TT>SRead()</TT>).
|
|
Otherwise, if you should set retry to kFullBufferNotRequired and SRead()
|
|
will return when there is some data read.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>int nread;
|
|
char buf[256];
|
|
|
|
while (1) {
|
|
nread = SRead(sockfd, buf, sizeof(buf), 15, kFullBufferNotRequired);
|
|
if (nread <= 0) {
|
|
if (nread == 0)
|
|
break; /* okay, EOF */
|
|
else if (nread == kTimeoutErr) {
|
|
fprintf(stderr, "timed-out\n");
|
|
break;
|
|
} else {
|
|
perror("read");
|
|
}
|
|
}
|
|
(void) write(1, buf, nread);
|
|
}</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SReadline"></A>SReadline</H5>
|
|
<TT>int SReadline(SReadlineInfo *srl, char *const linebuf, size_t linebufsize);</TT>
|
|
|
|
<P>It is often desirable to process data from sockets line by line, however
|
|
this is cumbersome to do on a socket descriptor. The <TT>SReadline()</TT>
|
|
function allows you to do this, so you can do one call of the function
|
|
and get back a line of input. It accomplishes this much the same
|
|
way the standard library's I/O routines do this, using a buffer.
|
|
However, it is usually not possible to determine if the standard library
|
|
takes the same special I/O measures on socket descriptors that sio does,
|
|
so using the standard library function <TT>fdopen()</TT> with a socket
|
|
descriptor may or may not work the way you want.
|
|
|
|
<P><TT>SReadline()</TT> needs to maintain state information for each call,
|
|
so a data structure is required. You must first call <TT><A HREF="#InitSReadlineInfo">InitSReadlineInfo()</A></TT>
|
|
to initialize a <TT>SReadlineInfo</TT> structure. After that, you
|
|
may call <TT>SReadline()</TT> repeatedly until it returns 0 to indicate
|
|
EOF. The function returns the number of characters in the input line, including
|
|
a newline (however, carriage return characters are omitted). You
|
|
must call <TT><A HREF="#DisposeSReadlineInfo">DisposeSReadlineInfo()</A></TT>
|
|
if you chose to let <TT><A HREF="#InitSReadlineInfo">InitSReadlineInfo()</A></TT>
|
|
use <TT>malloc()</TT> to allocate your buffer.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>SReadlineInfo sri;
|
|
char line[80];
|
|
int nread;
|
|
|
|
if (InitSReadlineInfo(&sri, sockfd, NULL, 512, 10) < 0) {
|
|
perror("malloc");
|
|
exit(1);
|
|
}
|
|
while (1) {
|
|
nread = SReadline(&sri, line, sizeof(line));
|
|
if (nread <= 0) {
|
|
if (nread == kTimeoutErr)
|
|
fprintf(stderr, "readline timed-out.\n");
|
|
else if (nread < 0)
|
|
perror("readline");
|
|
break;
|
|
}
|
|
line[nread] = '\0'; /* omit newline */
|
|
fprintf(stdout, "read [%s]\n", line);
|
|
}
|
|
DisposeSReadlineInfo(&sri);
|
|
(void) SClose(sockfd, 3);</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SRecv"></A>SRecv</H5>
|
|
<TT>int SRecv(int sfd, char *const buf0, size_t size, int fl, int tlen,
|
|
int retry);</TT>
|
|
|
|
<P>This is the corresponding wrapper for <TT>recv()</TT>, as <TT><A HREF="#SRead">SRead()</A></TT>
|
|
is for <TT>read()</TT>. You will never need this function, unless
|
|
you need the special receiving flags that <TT>recv()</TT> gives you.
|
|
<H5>
|
|
<A NAME="#SRecvfrom"></A>SRecvfrom</H5>
|
|
<TT>int SRecvfrom(int sfd, char *const buf, size_t size, int fl, struct
|
|
sockaddr_in *const fromAddr, int tlen);</TT>
|
|
|
|
<P>This is <TT>recvfrom()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
|
|
must be greater than zero). Like <TT>recvfrom()</TT>, it can return
|
|
0, (-1), or the number of bytes read, but in addition, it can also return
|
|
<TT>kTimeoutErr</TT>. Upon a timeout, the socket is still valid for additional
|
|
I/O.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>int nread;
|
|
char buf[80];
|
|
struct sockaddr_in remoteClientAddr;
|
|
|
|
nread = SRecvfrom(sockfd, buf, sizeof(buf), 0, &remoteClientAddr, 15);
|
|
if (nread <= 0) {
|
|
if (nread == kTimeoutErr)
|
|
fprintf(stderr, "recvfrom timed-out.\n");
|
|
else if (nread < 0)
|
|
perror("recvfrom");
|
|
}</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SRecvmsg"></A>SRecvmsg</H5>
|
|
<TT>int SRecvmsg(int sfd, void *const msg, int fl, int tlen);</TT>
|
|
|
|
<P>This is the corresponding wrapper for <TT>recvmsg()</TT>, as <TT><A HREF="#SRead">SRead()</A></TT>
|
|
is for <TT>read()</TT>.
|
|
<H5>
|
|
<A NAME="#SSend"></A>SSend</H5>
|
|
<TT>int SSend(int sfd, char *buf0, size_t size, int fl, int tlen);</TT>
|
|
|
|
<P>This is the corresponding wrapper for <TT>send()</TT>, as <TT><A HREF="#SWrite">SWrite()</A></TT>
|
|
is for <TT>write()</TT>. You will never need this function, unless
|
|
you need the special receiving flags that <TT>send()</TT> gives you.
|
|
<H5>
|
|
<A NAME="#SSendto"></A>SSendto</H5>
|
|
<TT>int SSendto(int sfd, const char *const buf, size_t size, int fl, const
|
|
struct sockaddr_in *const toAddr, int tlen);</TT>
|
|
|
|
<P>This is <TT>sendto()</TT> with a timeout of <I>tlen</I> seconds (<I>tlen</I>
|
|
must be greater than zero). Like <TT>sendto()</TT>, it can return
|
|
0, (-1), or the number of bytes sent, but in addition, it can also return
|
|
<TT>kTimeoutErr</TT>. Upon a timeout, the socket is still valid for additional
|
|
I/O.
|
|
|
|
<P>Since <TT>sendto()</TT> rarely blocks (only if the outgoing queue is
|
|
full), you probably do not want to use a timeout or bother with its associated
|
|
overhead; therefore <TT><A HREF="#Sendto">Sendto()</A></TT> would be a
|
|
better choice.
|
|
<H5>
|
|
<A NAME="#SSendtoByName"></A>SSendtoByName</H5>
|
|
<TT>int SSendtoByName(int sfd, const char *const buf, size_t size, int
|
|
fl, const char *const toAddrStr, int tlen);</TT>
|
|
|
|
<P>This is <TT><A HREF="#SSendto">SSendto()</A></TT>, only you can use
|
|
a textual internet address string instead of a <TT>struct sockaddr_in</TT>.
|
|
<H5>
|
|
<A NAME="#SWrite"></A>SWrite</H5>
|
|
<TT>int SWrite(int sfd, const char *const buf0, size_t size, int tlen);</TT>
|
|
|
|
<P>This is <TT>write()</TT> on a network socket with a timeout of <I>tlen</I>
|
|
seconds (<I>tlen</I> must be greater than zero). Like <TT>write()</TT>,
|
|
it can return 0, (-1), or the number of bytes sent, but in addition, it
|
|
can also return <TT>kTimeoutErr</TT>.
|
|
<H5>
|
|
<A NAME="SelectR"></A>SelectR</H5>
|
|
<TT>int SelectR(SelectSetPtr ssp, SelectSetPtr resultssp);</TT>
|
|
|
|
<P>This does a <TT>select()</TT> for reading with the file descriptors
|
|
in the specified <TT>SelectSet</TT>. Using a <TT>SelectSet</TT> ensures
|
|
that the first argument to select is always correct (the smallest correct
|
|
value, for speed) and <TT>SelectR()</TT> does not destroy the original
|
|
<TT>fd_set</TT> and <TT>timeval</TT> (it copies it to <I>resultssp</I>
|
|
before using <TT>select()</TT>).
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>SelectSet ss, selected;
|
|
|
|
SelectSetInit(&ss, 10.0);
|
|
SelectSetAdd(&ss, sockfd1);
|
|
SelectSetAdd(&ss, sockfd2);
|
|
rc = SelectR(&ss, &selected);
|
|
if ((rc > 0) && (FD_ISSET(sockfd2, &selected.fds))) ...</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="SelectW"></A>SelectW</H5>
|
|
<TT>int SelectW(SelectSetPtr ssp, SelectSetPtr resultssp);</TT>
|
|
|
|
<P>This does a <TT>select()</TT> for writing with the file descriptors
|
|
in the specified <TT>SelectSet</TT>. Using a <TT>SelectSet</TT> ensures
|
|
that the first argument to select is always correct (the smallest correct
|
|
value, for speed) and <TT>SelectW()</TT> does not destroy the original
|
|
<TT>fd_set</TT> and <TT>timeval</TT> (it copies it to <I>resultssp</I>
|
|
before using <TT>select()</TT>).
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>SelectSet ss, selected;
|
|
|
|
SelectSetInit(&ss, 10.0);
|
|
SelectSetAdd(&ss, sockfd1);
|
|
SelectSetAdd(&ss, sockfd2);
|
|
rc = SelectW(&ss, &selected);
|
|
if ((rc > 0) && (FD_ISSET(sockfd2, &selected.fds))) ...</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SelectSetAdd"></A>SelectSetAdd</H5>
|
|
<TT>void SelectSetAdd(SelectSetPtr const ssp, const int fd);</TT>
|
|
|
|
<P>This adds a descriptor to the set to select on.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>SelectSet ss;
|
|
|
|
SelectSetInit(&ss, 10.0);
|
|
SelectSetAdd(&ss, sockfd);</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SelectSetInit"></A>SelectSetInit</H5>
|
|
<TT>void SelectSetInit(SelectSetPtr const ssp, const double timeout);</TT>
|
|
|
|
<P>Before adding members to the <TT>SelectSet</TT> structure, it must be
|
|
initialized with this function. The timeout parameter initializes
|
|
the timeout to use for future <TT>Selects</TT>.
|
|
<H5>
|
|
<A NAME="#SelectSetRemove"></A>SelectSetRemove</H5>
|
|
<TT>void SelectSetRemove(SelectSetPtr const ssp, const int fd);</TT>
|
|
|
|
<P>This removes a descriptor from the set to select on. You will
|
|
need to do this just before you close a descriptor that was in the set.
|
|
<H5>
|
|
<A NAME="#Sendto"></A>Sendto</H5>
|
|
<TT>int Sendto(int sfd, const char *const buf, size_t size, const struct
|
|
sockaddr_in *const toAddr);</TT>
|
|
|
|
<P>This is a simple wrapper for <TT>sendto()</TT>, which only handles <TT>EINTR</TT>
|
|
for you. It does not worry about <TT>SIGPIPEs</TT>.
|
|
<H5>
|
|
<A NAME="#SendtoByName"></A>SendtoByName</H5>
|
|
<TT>int SendtoByName(int sfd, const char *const buf, size_t size, const
|
|
char *const toAddrStr);</TT>
|
|
|
|
<P>This is a simple wrapper for <TT>sendto()</TT>, which only handles <TT>EINTR</TT>
|
|
for you. In addition, you can use a textual internet address string
|
|
instead of a <TT>struct sockaddr_in</TT>.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>if (SendtoByName(sockfd, msg, sizeof(msg), "elwood.probe.net:13") > 0) ...</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SetSocketBufSize"></A>SetSocketBufSize</H5>
|
|
<TT>int SetSocketBufSize(int sockfd, size_t rsize, size_t ssize);</TT>
|
|
|
|
<P>This utility routine changes the size of the socket's internal buffers,
|
|
as maintained by the kernel (or socket library). It does this by
|
|
calling <TT>setsockopt()</TT> with the <TT>SO_RCVBUF</TT> and <TT>SO_SNDBUF</TT>
|
|
options, if they are defined. In the event they aren't defined, it
|
|
returns a negative result code. The operation is only performed if
|
|
the size is greater than zero, so if you only wanted to change the receive
|
|
buffer you could set <I>rsize</I> to greater than zero and <I>ssize</I>
|
|
to 0.
|
|
<H5>
|
|
<A NAME="#SetSocketLinger"></A>SetSocketLinger</H5>
|
|
<TT>int SetSocketLinger(const int fd, const int l_onoff, const int l_linger);</TT>
|
|
|
|
<P>This is an interface to the <TT>SO_LINGER</TT> socket option.
|
|
The <I>l_onoff</I> parameter is a boolean specifying whether linger is
|
|
on, and the <I>l_linger</I> parameter specifies the length of the linger
|
|
time if enabled.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>if (SetSocketLinger(sockfd, 1, 90) == 0) ...</PRE>
|
|
</UL>
|
|
|
|
<H5>
|
|
<A NAME="#SetSocketNagleAlgorithm"></A>SetSocketNagleAlgorithm</H5>
|
|
<TT>int SetSocketNagleAlgorithm(const int fd, const int onoff);</TT>
|
|
|
|
<P>This utility routine enables or disables the <I>Nagle Algorithm</I>
|
|
(<TT>TCP_NODELAY</TT> mode is off or on). Generally you won't care
|
|
about this, unless you're writing an interactive application like <I>telnet</I>,
|
|
<I>talk</I>, or <I>rlogin</I>, where response time is more important than
|
|
throughput.
|
|
|
|
<P><I>Example</I>:
|
|
<UL>
|
|
<PRE>if (SetSocketNagleAlgorithm(sockfd, 0) == 0) ...</PRE>
|
|
</UL>
|
|
|
|
<H3>
|
|
<A NAME="Sample"></A>Sample Code</H3>
|
|
Here is an example client and server that uses the <I>sio</I> library routines.
|
|
The server simply takes data and uppercases any lower case data, and returns
|
|
the result to the client. To try it on port number 5123, you could
|
|
run "<TT>ucase_s 5123</TT>" in one window, and "<TT>ucase_c localhost:5123</TT>"
|
|
in another.
|
|
<BR>
|
|
<H4>
|
|
Server:</H4>
|
|
|
|
<PRE>/* ucase_s.c */
|
|
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/wait.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#include "sio.h"
|
|
|
|
static void
|
|
ServeOneClient(int sockfd, struct sockaddr_in *cliAddr)
|
|
{
|
|
char buf[32], cliAddrStr[64];
|
|
int nread, nwrote, i;
|
|
|
|
printf("subserver[%d]: started, connected to %s.\n", (int) getpid(),
|
|
AddrToAddrStr(cliAddrStr, sizeof(cliAddrStr), cliAddr, 1, "<%h:%p>")
|
|
);
|
|
for (;;) {
|
|
nread = SRead(sockfd, buf, sizeof(buf), 15, kFullBufferNotRequired);
|
|
if (nread == 0) {
|
|
break;
|
|
} else if (nread < 0) {
|
|
fprintf(stderr, "subserver[%d]: read error: %s\n",
|
|
(int) getpid(), strerror(errno));
|
|
break;
|
|
}
|
|
for (i=0; i<nread; i++)
|
|
if (islower(buf[i]))
|
|
buf[i] = toupper(buf[i]);
|
|
nwrote = SWrite(sockfd, buf, nread, 15);
|
|
if (nwrote < 0) {
|
|
fprintf(stderr, "subserver[%d]: write error: %s\n",
|
|
(int) getpid(), strerror(errno));
|
|
break;
|
|
}
|
|
}
|
|
(void) SClose(sockfd, 10);
|
|
printf("subserver[%d]: done.\n", (int) getpid());
|
|
exit(0);
|
|
} /* ServeOneClient */
|
|
|
|
|
|
static void
|
|
Server(int port)
|
|
{
|
|
int sockfd, newsockfd;
|
|
struct sockaddr_in cliAddr;
|
|
int pid;
|
|
|
|
sockfd = SNewStreamServer(port, 3, kReUseAddrYes, 3);
|
|
if (sockfd < 0) {
|
|
perror("Server setup failed");
|
|
exit(1);
|
|
}
|
|
|
|
printf("server[%d]: started.\n", (int) getpid());
|
|
for(;;) {
|
|
while (waitpid(-1, NULL, WNOHANG) > 0) ;
|
|
newsockfd = SAccept(sockfd, &cliAddr, 5);
|
|
if (newsockfd < 0) {
|
|
if (newsockfd == kTimeoutErr)
|
|
printf("server[%d]: idle\n", (int) getpid());
|
|
else
|
|
fprintf(stderr, "server[%d]: accept error: %s\n",
|
|
(int) getpid(), strerror(errno));
|
|
} else if ((pid = fork()) < 0) {
|
|
fprintf(stderr, "server[%d]: fork error: %s\n",
|
|
(int) getpid(), strerror(errno));
|
|
exit(1);
|
|
} else if (pid == 0) {
|
|
ServeOneClient(newsockfd, &cliAddr);
|
|
exit(0);
|
|
} else {
|
|
/* Parent doesn't need it now. */
|
|
(void) close(newsockfd);
|
|
}
|
|
}
|
|
} /* Server */
|
|
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
int port;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
|
|
exit(2);
|
|
}
|
|
port = atoi(argv[1]);
|
|
Server(port);
|
|
exit(0);
|
|
} /* main */
|
|
</PRE>
|
|
|
|
<H4>
|
|
Client:</H4>
|
|
|
|
<PRE>/* ucase_c.c */
|
|
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/wait.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#include "sio.h"
|
|
|
|
static void
|
|
Client(char *serverAddrStr)
|
|
{
|
|
char buf[256];
|
|
int nread, nwrote, sockfd;
|
|
|
|
sockfd = SNewStreamClient();
|
|
if (sockfd < 0) {
|
|
fprintf(stderr, "client[%d]: socket error: %s\n",
|
|
(int) getpid(), strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
if (SConnectByName(sockfd, serverAddrStr, 15) < 0) {
|
|
fprintf(stderr, "client[%d]: could not connect to <%s>: %s\n",
|
|
(int) getpid(), serverAddrStr, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
printf("client[%d]: connected to <%s>.\n", (int) getpid(), serverAddrStr);
|
|
for (buf[sizeof(buf) - 1] = '\0';;) {
|
|
printf("client[%d]: Enter message to send -> ", (int) getpid());
|
|
if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
|
|
break;
|
|
buf[strlen(buf) - 1] = '\0'; /* Delete newline. */
|
|
if (buf[0] == '\0')
|
|
continue; /* Blank line. */
|
|
|
|
/* Send the request line to the server. */
|
|
nwrote = SWrite(sockfd, buf, strlen(buf), 15);
|
|
if (nwrote < 0) {
|
|
fprintf(stderr, "client[%d]: write error: %s\n",
|
|
(int) getpid(), strerror(errno));
|
|
break;
|
|
}
|
|
|
|
/* Wait for complete reply line */
|
|
nread = SRead(sockfd, buf, nwrote, 15, kFullBufferRequired);
|
|
if (nread == 0) {
|
|
fprintf(stderr, "client[%d]: no reply received (EOF).\n",
|
|
(int) getpid());
|
|
break;
|
|
} else if (nread < 0) {
|
|
fprintf(stderr, "client[%d]: read error: %s\n",
|
|
(int) getpid(), strerror(errno));
|
|
break;
|
|
}
|
|
buf[nread] = '\0';
|
|
fprintf(stdout, "client[%d]: received: %s\n",
|
|
(int) getpid(), buf);
|
|
}
|
|
(void) SClose(sockfd, 10);
|
|
printf("\nclient[%d]: done.\n", (int) getpid());
|
|
exit(0);
|
|
} /* Client */
|
|
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
int port;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Usage: %s <host:port>\n", argv[0]);
|
|
exit(2);
|
|
}
|
|
Client(argv[1]);
|
|
exit(0);
|
|
} /* main */
|
|
</PRE>
|
|
|
|
</BODY>
|
|
</HTML>
|