Now we can implement the server. The server side equivalent of the DistWebArray class is DistWebArrayImpl. This class interprets the protocol and manages the underlying collection. Here is its declaration.
class DistWebArrayImpl { public: DistWebArrayImpl() {} void operator()(RWvistream& istrm, RWvostream& ostrm); //1 void insert(RWvistream& istrm, RWvostream& ostrm); void find(RWvistream& istrm, RWvostream& ostrm); private: RWTValSlistDictionary<RWCString,Document*> dict_; //2 };
//1 | The parenthesis operator is the general interface for handling requests. The implementation of this operator extracts a string from istrm indicating the operation requested. The request is then delegated to the appropriate interface. |
//2 | The collection itself is kept here. |
The implementation uses only virtual streams; it is completely decoupled from the disposition of the data. This means we can use the same implementation with virtual streams attached to a file, for example, to read a set of initializing items. Also, we can use any type of virtual stream with this implementation.
The implementation of the member functions is straightforward.
void DistWebArrayImpl::operator()(RWvistream& i, RWvostream& o) { RWCString op; i >> op; if (op=="insert") { insert(i,o); } else if (op=="find") { find(i,o); } }
void DistWebArrayImpl::insert(RWvistream& istrm, RWvostream& ostrm) { RWCString key; Document *value; istrm >> key >> value; dict_.insertKeyAndValue(key,value); ostrm << RWCString("ok"); }
void DistWebArrayImpl::find(RWvistream& istrm, RWvostream& ostrm) { RWCString key; istrm >> key; ostrm << ( dict_.contains(key) ? dict_[key] : Document::defaultDocument() ); }
The server is a simple iterative server, which eliminates the need for locking the collection while accessing it.
int main() { try { RWWinSockInfo info; DistWebArrayImpl impl; //1 RWSocketListener listener( RWSockAddr(SERVER) ); //2 for(;;) { RWPortal portal = listener(); //3 RWPortalStreambuf sbuf(portal); //4 impl( RWpistream(&sbuf), RWpostream(&sbuf) ); //5 } } catch( const RWxmsg& msg ) { //6 cerr << "Error: " << msg.why() << endl; } return 0; }
//1 | Instantiate an implementation to hold the collection and implement the protocol. |
//2 | Start a listener to monitor the server port for incoming connections. As with the client programs, SERVER is a preprocessor symbol set in the makefile to the address where the server runs. An RWSocketListener accepts connections on a specified socket address. See the Tools.h++ Professional Class Reference for more information on the RWSocketListener class. |
//3 | Receive the next incoming connection. A portal is created to represent the connection. |
//4 | Attach the portal to a streambuf so that we can use virtual streams. |
//5 | Pass the client's request on to the implementation. |
//6 | Handle any errors that might occur by printing a message and giving up. This is not a great strategy for handling errors, but we use it here for simplicity. A better idea might be to separately catch any exceptions that are thrown inside the infinite loop, and handle those errors by simply closing the connection and continuing on. This strategy would handle situations like clients that died part way into the protocol. |
©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.