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. 28The library also takes steps to protect against a few common pitfalls that 29plague poorly written BSD Sockets programs. 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. 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> 89<H4> 90<A NAME="Connection_modes"></A>Connection modes</H4> 91A communications exchange between two end-points can be connection-oriented 92or connectionless. An connection-oriented exchange is characterized 93by a connection establishment, a series of messages, and a disconnection. 94A connectionless exchange is characterized by one or more independent messages. 95An analogy to a connection-oriented exchange would be a telephone call 96where one party establishes a connection to another, and a conversation 97ensues. 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> 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. After connection establishment, 105the conversation consists of one long sequence of data bytes, which is 106known as a <I>stream</I>. 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. 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. 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. 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. 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> 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. A socket must be either a client or a server, although 136after communications is established the difference is arbitrary. 137A server socket is one that waits for contact to be initiated by a client 138on a mutually agreed upon address and port number. A client socket 139initiates the first communication by sending to an existing server socket. 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. 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>. 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>. 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>. 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> 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. 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. Since 173each datagram communication is independent, these routines use an address 174specifier along with each call. 175<BR> 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> 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>. 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", &addr, 21); 312result = AddrStrToAddr("ftp.probe.net:21", &addr, 21); 313result = AddrStrToAddr("206.28.166.234:21", &addr, 21); 314result = AddrStrToAddr("ftp://ftp.probe.net", &addr, 0); 315result = AddrStrToAddr("21@ftp.probe.net", &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. 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. The 347<I>dst</I> parameter is the string to write the results to, and is always 348null-terminated. 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), &sin, 1, "%h"), stdout); 356fputs(AddrToAddrStr(str, sizeof(str), &sin, 1, "%h:%p"), stdout); 357fputs(AddrToAddrStr(str, sizeof(str), &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>. 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>. 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). It does this by 383calling <TT>getsockopt()</TT> with the <TT>SO_RCVBUF</TT> and <TT>SO_SNDBUF</TT> 384options, if they are defined. 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, &rsize, &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, &lingtime) > 0) 406 /* 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. 422The <I>sockfd</I> parameter specifies the socket that will be used for 423line buffering. 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. 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. 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(&sri, sockfd, NULL, 512, 10) < 0) 443 perror("malloc 512 bytes failed"); 444...or... 445(void) InitSReadlineInfo(&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). If no new connection 454is accepted, <TT>kTimeoutErr</TT> is returned, otherwise a new socket or 455(-1) is returned. 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). 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). 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. 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. <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). 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). 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>. 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. 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. 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 /* 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. This function is just <TT>socket(AF_INET, SOCK_STREAM, 5500)</TT>. 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. 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. 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 /* 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). 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. 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. If you set retry to <TT>kFullBufferRequiredExceptLast</TT>, 596you get the partial record (and EOF on the next <TT>SRead()</TT>). 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 nread = SRead(sockfd, buf, sizeof(buf), 15, kFullBufferNotRequired); 607 if (nread <= 0) { 608 if (nread == 0) 609 break; /* okay, EOF */ 610 else if (nread == kTimeoutErr) { 611 fprintf(stderr, "timed-out\n"); 612 break; 613 } else { 614 perror("read"); 615 } 616 } 617 (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. 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. It accomplishes this much the same 629way the standard library's I/O routines do this, using a buffer. 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. You must first call <TT><A HREF="#InitSReadlineInfo">InitSReadlineInfo()</A></TT> 637to initialize a <TT>SReadlineInfo</TT> structure. 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). 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(&sri, sockfd, NULL, 512, 10) < 0) { 652 perror("malloc"); 653 exit(1); 654} 655while (1) { 656 nread = SReadline(&sri, line, sizeof(line)); 657 if (nread <= 0) { 658 if (nread == kTimeoutErr) 659 fprintf(stderr, "readline timed-out.\n"); 660 else if (nread < 0) 661 perror("readline"); 662 break; 663 } 664 line[nread] = '\0'; /* omit newline */ 665 fprintf(stdout, "read [%s]\n", line); 666} 667DisposeSReadlineInfo(&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>. 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). 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, &remoteClientAddr, 15); 697if (nread <= 0) { 698 if (nread == kTimeoutErr) 699 fprintf(stderr, "recvfrom timed-out.\n"); 700 else if (nread < 0) 701 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>. 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). 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). 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>. 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(&ss, 10.0); 764SelectSetAdd(&ss, sockfd1); 765SelectSetAdd(&ss, sockfd2); 766rc = SelectR(&ss, &selected); 767if ((rc > 0) && (FD_ISSET(sockfd2, &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>. 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(&ss, 10.0); 786SelectSetAdd(&ss, sockfd1); 787SelectSetAdd(&ss, sockfd2); 788rc = SelectW(&ss, &selected); 789if ((rc > 0) && (FD_ISSET(sockfd2, &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(&ss, 10.0); 803SelectSetAdd(&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. 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. 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. 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. 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). It does this by 846calling <TT>setsockopt()</TT> with the <TT>SO_RCVBUF</TT> and <TT>SO_SNDBUF</TT> 847options, if they are defined. In the event they aren't defined, it 848returns a negative result code. 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. 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). 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. 884The server simply takes data and uppercases any lower case data, and returns 885the result to the client. 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> 889<H4> 890Server:</H4> 891 892<PRE>/* ucase_s.c */ 893 894#include <unistd.h> 895#include <sys/types.h> 896#include <sys/socket.h> 897#include <sys/wait.h> 898#include <netinet/in.h> 899#include <arpa/inet.h> 900#include <errno.h> 901#include <stdio.h> 902#include <string.h> 903#include <stdlib.h> 904#include <time.h> 905 906#include "sio.h" 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("subserver[%d]: started, connected to %s.\n", (int) getpid(), 915 AddrToAddrStr(cliAddrStr, sizeof(cliAddrStr), cliAddr, 1, "<%h:%p>") 916 ); 917 for (;;) { 918 nread = SRead(sockfd, buf, sizeof(buf), 15, kFullBufferNotRequired); 919 if (nread == 0) { 920 break; 921 } else if (nread < 0) { 922 fprintf(stderr, "subserver[%d]: read error: %s\n", 923 (int) getpid(), strerror(errno)); 924 break; 925 } 926 for (i=0; i<nread; i++) 927 if (islower(buf[i])) 928 buf[i] = toupper(buf[i]); 929 nwrote = SWrite(sockfd, buf, nread, 15); 930 if (nwrote < 0) { 931 fprintf(stderr, "subserver[%d]: write error: %s\n", 932 (int) getpid(), strerror(errno)); 933 break; 934 } 935 } 936 (void) SClose(sockfd, 10); 937 printf("subserver[%d]: done.\n", (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 < 0) { 951 perror("Server setup failed"); 952 exit(1); 953 } 954 955 printf("server[%d]: started.\n", (int) getpid()); 956 for(;;) { 957 while (waitpid(-1, NULL, WNOHANG) > 0) ; 958 newsockfd = SAccept(sockfd, &cliAddr, 5); 959 if (newsockfd < 0) { 960 if (newsockfd == kTimeoutErr) 961 printf("server[%d]: idle\n", (int) getpid()); 962 else 963 fprintf(stderr, "server[%d]: accept error: %s\n", 964 (int) getpid(), strerror(errno)); 965 } else if ((pid = fork()) < 0) { 966 fprintf(stderr, "server[%d]: fork error: %s\n", 967 (int) getpid(), strerror(errno)); 968 exit(1); 969 } else if (pid == 0) { 970 ServeOneClient(newsockfd, &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 < 2) { 986 fprintf(stderr, "Usage: %s <port>\n", 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 <unistd.h> 1001#include <sys/types.h> 1002#include <sys/socket.h> 1003#include <sys/wait.h> 1004#include <netinet/in.h> 1005#include <arpa/inet.h> 1006#include <errno.h> 1007#include <stdio.h> 1008#include <string.h> 1009#include <stdlib.h> 1010#include <time.h> 1011 1012#include "sio.h" 1013 1014static void 1015Client(char *serverAddrStr) 1016{ 1017 char buf[256]; 1018 int nread, nwrote, sockfd; 1019 1020 sockfd = SNewStreamClient(); 1021 if (sockfd < 0) { 1022 fprintf(stderr, "client[%d]: socket error: %s\n", 1023 (int) getpid(), strerror(errno)); 1024 exit(1); 1025 } 1026 1027 if (SConnectByName(sockfd, serverAddrStr, 15) < 0) { 1028 fprintf(stderr, "client[%d]: could not connect to <%s>: %s\n", 1029 (int) getpid(), serverAddrStr, strerror(errno)); 1030 exit(1); 1031 } 1032 1033 printf("client[%d]: connected to <%s>.\n", (int) getpid(), serverAddrStr); 1034 for (buf[sizeof(buf) - 1] = '\0';;) { 1035 printf("client[%d]: Enter message to send -> ", (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 < 0) { 1045 fprintf(stderr, "client[%d]: write error: %s\n", 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, "client[%d]: no reply received (EOF).\n", 1054 (int) getpid()); 1055 break; 1056 } else if (nread < 0) { 1057 fprintf(stderr, "client[%d]: read error: %s\n", 1058 (int) getpid(), strerror(errno)); 1059 break; 1060 } 1061 buf[nread] = '\0'; 1062 fprintf(stdout, "client[%d]: received: %s\n", 1063 (int) getpid(), buf); 1064 } 1065 (void) SClose(sockfd, 10); 1066 printf("\nclient[%d]: done.\n", (int) getpid()); 1067 exit(0); 1068} /* Client */ 1069 1070 1071void 1072main(int argc, char **argv) 1073{ 1074 int port; 1075 1076 if (argc < 2) { 1077 fprintf(stderr, "Usage: %s <host:port>\n", argv[0]); 1078 exit(2); 1079 } 1080 Client(argv[1]); 1081 exit(0); 1082} /* main */ 1083</PRE> 1084 1085</BODY> 1086</HTML> 1087