📄 streams.wvsgml
字号:
WVPART(streams, The Streams Library, WVCHAPTER(wvstream, WvStream - communications fundamentals, WvStream is the base class from which (not surprisingly) most of the WvStreams library is derived. It defines the basic properties of a stream: a sequence of bytes that may or may not be ready for reading or writing at any given time. WvStream is a perfectly useful class all by itself; it can read, write, and wait on a Unix fd (file descriptor), which is sufficient for nearly all communications in Unix-like systems. The other WvStream classes do basically the same thing, but create and manipulate their fds automatically instead of requiring you to do it. WVSECT1(wvstreamsimple, Basic WvStream Examples, The following example program reads from stdin and echos the results back to stdout with a bit of added commentary. Remember that in Unix, fd 0 is stdin, and fd 1 is stdout. WVEXAMPLE(wvstreamex.cc) isok() ("is okay") returns true as long as the stream is still alive. If you press CTRL-D as the input to a program, this sends an end-of-file message and the stream becomes "not okay." (isok() == false). The getline() function reads a character string of arbitrary length using <link linkend="wvbuffer">WvBuffer</link>, up to but not including the first newline. The (-1) tells getline() to wait forever for the newline if necessary. Try changing it to 5000 (5 seconds) and see how that affects the operation of the program. Whenever possible, WvStreams tries to shelter you from weird Unix-specific information like fd 0 and fd 1. There is a special WvStream pointer called wvcon which points to both stdin and stdout. (Actually, since it points at both fd 0 and fd 1, wvcon needs to be a <link linkend="wvsplitstream">WvSplitStream</link> object, but don't worry about that just yet.) You can reassign wvcon to any other stream if you like -- this will let you essentially redirect stdin and stdout for the WvStreams classes that use it. Here's an example of how you can use wvcon to simplify the above sample program. WVEXAMPLE(wvstreamex2.cc) ) WVSECT1(wvstreamrw, Reading and Writing, Here's a sample program that uses the read() and write() member functions. Usually, these functions are used more often than getline() because WvStreams are commonly used to manipulate binary data rather than line-by-line data. WVEXAMPLE(wvstreamex3.cc) Unlike getline(), read() isn't very smart. You must supply it with a buffer and tell it how large the buffer is. However, there's no guarantee that read() will fill the entire buffer or any of it -- you should _always_ check the return value (numread, in this case) to find out how many bytes were read. You'll find out later (when we talk about <link linkend="wvstreamselect">select()</link>) that this is actually the right thing to do -- the last thing you want in most WvStreams programs is for read() to sit doing nothing until a buffer fills up. On the other hand, the write() function has a built-in buffer, so as long as the stream is alive, the return value of write() should always be the same as the length you provide as the second parameter. The print() function works sort of like printf(). It uses the <link linkend="wvstringexamples">complex constructor for WvString</link> to type-safely format strings, and then the write() function to send them out to the stream. ) WVSECT1(wvstreamselect, Waiting on a stream with select(), The previous example was actually not completely correct -- it works, but depending on the wvcon implementation, it could accidentally use 100% CPU time. (The default wvcon implementation doesn't actually have this "problem," but that's a coincidence and might change in the future.) Remember that read() is not guaranteed to fill the input buffer. In fact, it's not even guaranteed to read any bytes at all. So in theory, the previous example might find itself endlessly looping, calling isok() and read() over and over until some input is received. That will work, of course, but it wastes a lot of CPU time. What we should really do is tell WvStream to wait until some data is available, and only then call read(). Here's how. WVEXAMPLE(wvstreamex4.cc) Note again that the (-1) parameter to select() means "wait forever." In this case, it doesn't really matter if you set it to 5000 (5 seconds), for example, because we'll simply wait five seconds and restart the loop. If we do select(0), select() doesn't wait at all. This isn't as pointless as it seems: select() returns true or false depending whether the stream was ready or not, so select(0) is a good way to test if a stream is ready without actually stopping your program. You can also select() to see if a stream is writable, but this is used much less often since WvStream now has write buffers anyway (so you can always write to a stream without delaying). Sometimes it can be useful, though: for example, a TCP connection is not "writable" until it's connected to the remote host. That makes sense, if you think about it. It's not readable either, of course, but it doesn't necessarily become readable as soon as it connects. A connected TCP stream is always writable. You can check for (or wait for) readability and writability at the same time. There is also a test for a stream "exception", but no one has ever used that, so I don't know what it does. The prototype for select() actually looks like this: WVCMD(bool select(time_t msec_timeout, bool readable=true, bool writable=false, bool isexception=false);) That means normally, we want to check whether a stream is readable, and we don't care if it's writable or has an exception. To check whether a stream is writable, for example, use a line like this: WVCMD(wvcon->select(0, false, true, false);) To wait up to 5 seconds until the stream is either readable or writable, try this: WVCMD(wvcon->select(5000, true, true, false);) Now, you're probably wondering why read() can't just wait for data and write() can't just send it out, saving us all a lot of trouble. Well, that's all fine and good for single-tasking one-stream programs, but WvStreams was designed to handle lots of simultaneous connections -- that's when select() really gets useful. We'll talk about that later, in the section about <link linkend="wvstreamlist">WvStreamList</link>. ) WVSECT1(wvstreamcallbacks, Callback Functions, Unless you've skipped ahead to the <link linkend="wvstreamlist">WvStreamList</link> section, this section will seem even more useless than the one on <link linkend="wvstreamselect">select()</link>. But trust us, when you have lots of different streams talking to each other later on, you'll be really glad to have callback functions around. Here's a quick example, using one of the predefined stream callbacks called autoforward(). WVEXAMPLE(wvstreamex5.cc) The above program does almost exactly what the previous ones did, except that instead of manually reading and writing strings, we tell the stream that its "default operation" is to auto-forward its data to wvcon... which is, in this case, itself. The result is that we forward stdin to stdout as before, only without the extra text surrounding it. Almost all WvStreams programs end up using callbacks for most of their work. That's because with callbacks, each stream knows what to do with its own data, and you don't have to say it in the main program. That's what object-oriented programming is all about, after all. In fact, the main loop in most WvStreams programs ends up looking a lot like the main loop in the program above: wait until the stream is ready, then run its callback function. What does autoforward() do, exactly? Let's write a callback function ourselves and demonstrate one possibility. WVEXAMPLE(wvstreamex6.cc) Note that mycallback() calls getline() with a (0) delay parameter; you almost never want a callback function to wait for something (although it certainly can, if you really want). Instead, if getline can't find a whole line of text to read, we simply return from the callback and wait until next time. This brings up an interesting point about select() and getline(): select() might say yes, the stream is ready for reading, but getline() might still return NULL. Why? Because getline() looks for a terminating newline. If the only input is the letters "abcdefg" (without a trailing newline character) then the stream really is ready for reading, but getline hasn't retrieved a whole line of input. Don't worry, though, running getline() once clears the input-ready condition, so the callback will only get called again when more data is received. But whatever you do, don't be fooled into thinking that just because you're in the callback function, that data will always be returned on a read() or getline(). Always, _always_ check the return values first. ) WVSECT1(wvstreamdelay, Delays and Timeouts, We should mention that you can use the select() function for millisecond-resolution delays, or to timeout when no data is received for a certain amount of time. The following example babbles something at you after every second of no input, and exits if you don't say anything for ten seconds.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -