import java.io.*; import java.net.*; /** * Title: Safe Object Streams * Copyright: Copyright (c) 2004 * Company: Superliminal Software * @author Melinda Green * @version 1.0 * * Description: * A helper object that safely creates pairs of ObjectInput/OutputStream * objects. This requires a tricky bit of client/server handshaking * to set up. The purpose of this class is to make this a safe and easy process. * Note: when used on one end of a Socket, the other end must also. * * The reason this is tricky is * because threads will easily deadlock whenever an ObjectInputStream is constructed * before the corresponding ObjectOutputStream is created at the other end * of the socket. one difficult question is how the client and server can signal * each other over their shared socket that they've created their * ObjectOutputStreams without corrupting the socket's ability to send objects. * That is handled that by using PushbackInputStreams to perform blocking reads * to wait for the other's signal, and then unreading the data and then using * the pristine PushbackInputStream as the ObjectInputStream's data source. */ public class SOS { private ObjectOutputStream m_oos; private InputStream m_is; private ObjectInputStream m_ois=null; /** * Creates a Safe Object Streams object. * @param soc one end of a socket to transmit objects. * note: the other end of the socket must be similarly treated. */ public SOS(Socket soc) { try { OutputStream os = soc.getOutputStream(); m_oos = new ObjectOutputStream(os); m_oos.flush(); InputStream is = soc.getInputStream(); PushbackInputStream pis = new PushbackInputStream(is); int got = pis.read(); // blocking read in order to wait until ok-to-read signal received from other end pis.unread(got); m_is = pis; } catch(Exception e) { System.err.println("SOS exception " + e); } // should now be safe to construct ObjectInputStream without risk of deadlock } public ObjectOutputStream getOutputStream() { return m_oos; } public ObjectInputStream getInputStream() { // it must now be safe to construct object input stream because constructor must have // unblocked after it connected to the other end. if(m_ois != null) return m_ois; try { m_ois = new ObjectInputStream(m_is); } catch(Exception e) { System.err.println("SOS exception " + e); } return m_ois; } // // EVERYTHING FROM HERE DOWN IS TEST CODE // private static final int TEST_PORT = 7777; private static void startServer() { try { final ServerSocket ss = new ServerSocket(TEST_PORT); new Thread() { public void run() { while(true) { try { SOS sos = new SOS(ss.accept()); ObjectInputStream ois = sos.getInputStream(); Object obj = ois.readObject(); System.out.println("SOS test server read obj from SOS: " + obj); } catch(Exception e) { System.err.println("server exception " + e); } } } }.start(); } catch(Exception e) { System.err.println("server exception " + e); } } /** * the simplest possible example client/server program that tests SOS objects. */ public static void main(String args[]) { Object to_send = "THIS IS A MESSAGE FROM CLIENT TO SERVER"; // could be any serializable object try { startServer(); Socket s = new Socket("localhost", TEST_PORT); System.out.println("cliet writing to SOS: " + to_send); SOS sos = new SOS(s); sos.getOutputStream().writeObject(to_send); } catch(Exception e) { System.err.println("client exception " + e); } } }