SE550 Practice Midterm sample solution 1. The client/server model is a point-to-point communications model between two hosts. The server initially passively listens for incoming connections. The client initiates the dialogue by connecting to the server. 2. Distributed programming: programming for multiple hosts communiting via a network. Parallel programming: programming for multiple processors communicating via shared memory. Concurrent programming: programming for multiple threads communicating via shared memory. 3. a) client -> server: LOOKUP klee server -> client: ADDRESS 140.192.32.63 client -> server: LOOKUP foobar server -> client: UNKNOWN HOST client -> server: QUIT b) Address of klee is 140.192.32.63 Address of foobar is unknown 4. public void run () { try { ... } catch (final IOException ex) { } finally { try { out.close (); in.close (); socket.close (); } catch (final IOException ex) { } } 5. ::= | ::= "LOOKUP" ::= "QUIT" | ::= " " ::= + ::= | | ::= "a" | ... | "z" | "A" | ... | "Z" ::= "0" | ... | "9" ::= "-" | "." ::= | | ::= "ADDRESS"
::= "UNKNOWN" "HOST" ::= "ERROR"
::= "." "." "." ::= ( ()? )? 6. token { | | | | | | | )+> | } public production void response () { addressResponse () | unknownResponse () | errorResponse () } production void addressResponse () { { byte[] address; }
address = address () { handleAddressResponse (address[0], address[1], address[2], address[3]); } } production byte[] address () { { byte[] result = new byte[4]; byte tmp; } tmp = byte () { result[0] = tmp; } tmp = byte () { result[1] = tmp; } tmp = byte () { result[2] = tmp; } tmp = byte () { result[3] = tmp; } { return result; } } production byte byte () { { Token t; } t = { return Byte.parseByte (t.image); } } production void unknownResponse () { { handleUnknownResponse (); } } production void errorResponse () { { handleErrorResponse (); } } 7. At the sender: Keep a lookup table (called a cache) which associates object names (called handles) with objects, initially empty. To serialize base types (int, bool, etc. send the appropriate binary representation). To serialize an object: * if the object is in the cache, send the associated handle. * otherwise, create a new handle, put the (object, handle) pair into the cache, send the handle, send the object's class name, then recursively serialize the fields. At the receiver: Keep a lookup table (called a cache) which associates objects with handles, initially empty. To unserialize a base type, receive the appropriate binary representation. To unserialize an object: * if we recieve just a handle, look it up in the cache. * otherwise, get the new handle, the object's class name, create a new object of that class, put the (handle, object) pair in the chache, then recursively unserialize the fields. The cache is necessary in order to deal with cyclic structures. If we didn't use a cache, then any cyclic heap would cause an infinite loop in the serialization algorithm. 8. A stub object is the implementation of a remote pointer in Java. A remote pointer is defined by: * a remote interface (which extends Remote) * a remote object which implements the interface. The stub object: * implements the remote interface * contains a socket connection to the RMI infrastructure at the host containing the remote object * is responsible for remote method invocation: when a remote method is called, the stub serializes (or marshals) the arguments, then waits for the results, unserializes (or unmarshals) them, and returns the results. 9. Where to start... most any answer will do, including: Get rid of the I/O Stream vs Reader/Writer distinction. Have the compiler check that objects which implement Serializable really are serializable. Have the default serialization algorithm insist that serializable objects be immutable. Fix the problems with interruptable I/O.