|
Using wxWidgets' streams, it is easy to move and transform large amounts of data with only a few lines of code. Consider the task of sending a file over a socket. One approach would be to open the file, read the entire file into memory, and then send the memory block to the socket. This approach is fine for small files, but reading a large multi-megabyte file into memory might not be very speedy on a slower, low-memory computer. Furthermore, what if you were required to also compress the file as you send it to reduce network traffic? Reading a large file into memory, compressing it all at once, and then writing it to a socket simply would not be efficient or practical. A second approach might be to read the file in small pieces, such as several kilobytes, compress the pieces, and then output these pieces over the socket. Unfortunately, compressing the individual pieces is not going to be as efficient as compressing the whole file at once. A refinement might be to maintain stateful compression streams from one piece to the next (where the compression of one frame can use compression information from the previous, most notably avoiding the need for each frame to have its own header), but you're already looking at dozens of lines of code and the delicate synchronization of reading from the file, compressing, and sending. With wxWidgets, there is a better way. Because wxWidgets provides both wxSocketInputStream and wxSocket OutputStream classes, it is very easy to stream data in and out of sockets through other streams. Consider that wxWidgets provides streams for files, strings, text, memory, and Zlib compression, and some very interesting possibilities become apparent for using sockets in unique and powerful ways. If we revisit the file sending with compression problem with streams on our tool belt, a new solution is available. To send a file, we can stream data from the file to the Zlib compression to the socket, and suddenly we have stateful file compression, resulting in a completely compressed file being sent without reading more than a few kilobytes at a time. On the receiving end, we can stream the data from the socket through the Zlib decompression, and finally into the output file. All this can be done in only a few lines of code. We will do our file streaming in a thread so that we can block on the socket operations and not worry about blocking the GUI or running into the 100% CPU usage issue detailed earlier, which is quite possible if we were to send large multi-megabyte files. The complete socket stream sources can be found on the accompanying CD-ROM in examples/chap18. File Sending ThreadThe sending thread demonstrates using streams allocated dynamically on the heap. FileSendThread derives from wxTHRead. FileSendThread::FileSendThread(wxString Filename, wxSocketBase* Socket) { m_Filename = Filename; m_Socket = Socket; Create(); Run(); } void* FileSendThread::Entry() { // If we can't write anything for 10 seconds, assume a timeout m_Socket->SetTimeout(10); // Wait for all the data to write, blocking on the socket calls m_Socket->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); // Read from the specified file wxFileInputStream* FileInputStream = new wxFileInputStream(m_Filename); // An output stream writing to the socket wxSocketOutputStream* SocketOutputStream = new wxSocketOutputStream(*m_Socket); // The results of the compression will be written to the // socket stream wxZlibOutputStream* ZlibOutputStream = new wxZlibOutputStream(*SocketOutputStream); // Write the results of the zlib decompression to the file stream ZlibOutputStream->Write(*FileInputStream); // Write all data ZlibOutputStream->Sync(); // Destroying will send Zlib compression EOF delete ZlibOutputStream; // Clean up delete SocketOutputStream; delete FileInputStream; return NULL; } File Receiving ThreadThe receiving thread demonstrates using streams allocated on the stack. FileReceiveThread derives from wxThread. FileReceiveThread::FileReceiveThread(wxString Filename, wxSocketBase* Socket) { m_Filename = Filename; m_Socket = Socket; Create(); Run(); } void* FileReceiveThread::Entry() { // If we don't receive anything for 10 seconds, assume a timeout m_Socket->SetTimeout(10); // Wait for some data to come in, or for an error // and block on the socket calls m_Socket->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); // Output to the specified file wxFileOutputStream FileOutputStream(m_Filename); // Stream data in from the socket wxSocketInputStream SocketInputStream(*m_Socket); // The zlib decompression will decompress data from the // socket stream wxZlibInputStream ZlibInputStream(SocketInputStream); // Write to the file stream the results of reading from the // zlib input stream FileOutputStream.Write(ZlibInputStream); return NULL; } |
|