package edu.vt.marian.uip; import java.io.*; import java.net.*; import java.util.*; import edu.vt.marian.common.*; /** class name: client_uip_thread class description: this class is responsible to create the connection with the server part, and do the actual function passing through the network uses the services of class(es): debug, client_uip, uip_server, rpc_function designer(s): Jianxin Zhao (jxzhao@csgrad.cs.vt.edu) implementator(s): Xuelei Sun (xusun@csgrad.cs.vt.edu) finished time: December 8, 1998 known bugs: JDK version: 1.1.5 side effects: */ public class client_uip_thread extends Thread { public final static int OK = 0; public final static int NULL_PARAMETER = 1; public final static int INVALID_VERSION = 2; public final static int ERROR_CREAT_LISTEN_SOCKET = 3; public final static int ERROR_SET_SOCKET_TIME_OUT = 4; public final static int ERROR_CREATE_OUTBOUND_SOCKET = 5; public final static int NEW_SESSION_NOT_SENT = 6; public final static int LISTEN_TIME_OUT = 7; public final static int LISTEN_FAILED = 8; public final static int CREATE_OUTBOUND_STREAM_FAILED = 9; public final static int SEND_FUNCTION_FAILED = 10; public final static int REPLY_NOT_ACK = 11; public final static int RECEIVE_ACK_ERROR = 12; public final static int TIME_OUT_WAITING_ACK = 13; public final static int ERROR_READ_FUNCTION_FROM_SERVER = 14; public final static int SERVER_DISCONNECT = 15; public final static int EXIT = 16; public final static int NULL_FUNCTION = -1; public final static int INVALID_FUNCTION = -2; public final static int ERROR_WRITE_ACK = -3; /** This is for debugging */ Debug debug; /** Client uip, the class which create the thread */ private client_uip cu; /** version number, 1 is hard coded, 2 is flexible */ private int version; /** status flag: OK NULL_PARAMETER INVALID_VERSION ERROR_CREAT_LISTEN_SOCKET ERROR_SET_SOCKET_TIMEOUT ERROR_CREAT_OUTBOUND_SOCKET NEW_SESSION_NOT_SENT LISTEN_TIME_OUT LISTEN_FAILED CREATE_OUTBOUND_STREAM_FAILED SEND_FUNCTION_FAILED REPLY_NOT_ACK RECEIVE_ACK_ERROR TIME_OUT_WAITING_ACK ERROR_READ_FUNCTION_FROM_SERVER SERVER_DISCONNECT EXIT */ private int status; /** the socket responsible to send function out, and accept the acknowledgement */ private Socket outbound_socket = null; /** the socket responsible to receive function in, and send the acknowledgement */ private Socket inbound_socket = null; /** the server socket responsible to wait for the server connection */ private ServerSocket listen_socket = null; /** those are the streams of sockets */ private BufferedInputStream outbound_bis, inbound_bis; private BufferedOutputStream outbound_bos, inbound_bos; /** method description: this constructor will create a client_uip_thread object based on the information given by the parameters uses the services of class(es): Debug, client_uip, uip_server input parameter(s): client_uip cu, the client uip which the creates the thread uip_server us, the uip server which stores the information of the server int version, the connection version, 1 hard coded, 2 flexible Debug debug, debug object output parameter(s): none return value: none synchronization consideration: none */ public client_uip_thread (client_uip cu, uip_server us, int version, Debug debug) { //set the debug this.debug = debug; //check and the client_uip and uip_server if ( (cu == null) || (us == null) ) { debug.dumpTrace("client_uip_thread.[constructor]: null parameter warning."); status = NULL_PARAMETER; return; } this.cu = cu; //set the version this.version = version; //create server socket try { listen_socket = new ServerSocket(0); } catch (Exception e) { debug.dumpTrace("client_uip_thread.[constructor]: create server socket failed: " + e.toString()); status = ERROR_CREAT_LISTEN_SOCKET; return; } try { listen_socket.setSoTimeout(60000); } catch (Exception e) { debug.dumpTrace("client_uip_thread.[constructor]: set server socket timeout failed."); status = ERROR_SET_SOCKET_TIME_OUT; return; } //create outbound socket try { outbound_socket = new Socket(us.get_hostname(), us.get_port()); } catch (Exception e1) { debug.dumpTrace("client_uip_thread.[constructor]: unknown server [" + us.get_hostname() + ", " + us.get_port() + "]."); status = ERROR_CREATE_OUTBOUND_SOCKET; return; } try { outbound_socket.setSoTimeout(60000); } catch (Exception e) { debug.dumpTrace("client_uip_thread.[constructor]: outbound set time out failed."); status = ERROR_SET_SOCKET_TIME_OUT; return; } //create outbound socket streams try { outbound_bos = new BufferedOutputStream(outbound_socket.getOutputStream()); outbound_bis = new BufferedInputStream(outbound_socket.getInputStream()); } catch (IOException e) { debug.dumpTrace("client_uip_thread.[constructor]: create outbound stream failed: " + e.toString()); status = CREATE_OUTBOUND_STREAM_FAILED; return; } status = OK; //create and send rpc function new_session rpc_function rf = new rpc_function(debug); rf.set_name("new_session"); parameter p = new parameter("version", "INTEGER", new Integer(1), debug); rf.add_parameter(p); p = new parameter("port", "INTEGER", new Integer(listen_socket.getLocalPort()), debug); rf.add_parameter(p); int Err; if ( (Err = rpc_call(rf)) != OK) { debug.dumpTrace("client_uip_thread.[constructor]: send new_session failed, code " + Err + "."); status = NEW_SESSION_NOT_SENT; return; } //accept the connection from the server try { inbound_socket = listen_socket.accept(); } catch (InterruptedIOException e2) { status = LISTEN_TIME_OUT; return; } catch (IOException e) { status = LISTEN_FAILED; return; } //create inbound socket streams try { inbound_bos = new BufferedOutputStream(inbound_socket.getOutputStream()); inbound_bis = new BufferedInputStream(inbound_socket.getInputStream()); } catch (IOException e) { debug.dumpTrace("client_uip_thread.[constructor]: create inbound stream failed: " + e.toString()); status = CREATE_OUTBOUND_STREAM_FAILED; return; } status = OK; //start the thread this.start(); } /** method description: this method provide the smooth exit of the thread uses the services of class(es): Debug, rpc_function input parameter(s): String condition output parameter(s): none return value: int OK synchronization consideration: none */ public int exit (String condition) { debug.dumpTrace("client_uip_thread.exit(): ready to exit."); ////DEBUG //stop the thread this.stop(); debug.dumpTrace("client_uip_thread.exit(): after stopping the thread."); ////DEBUG if (status == OK) { //send end_session rpc_function rf = new rpc_function(debug); rf.set_name("end_session"); rpc_call(rf); } debug.dumpTrace("client_uip_thread.exit(): after sending end_session to server."); ////DEBUG //close sockets try { listen_socket.close(); outbound_socket.close(); inbound_socket.close(); } catch (Exception e) { ; } status = EXIT; debug.dumpTrace("client_uip_thread.exit(): after closing all the sockets."); ////DEBUG return OK; } /** method description: this method return current status uses the services of class(es): none input parameter(s): none output parameter(s): none return value: none synchronization consideration: none */ public int get_status() { return status; } /** method description: this method do the actural function passing from client to server uses the services of class(es): rpc_function, debug input parameter(s): rpc_function rf; output parameter(s): none return value: OK NULL_FUNCTION INVALID_FUNCTION OUTBOUND_STREAM_CREATE_FAILED SEND_FUNCTION_FAILED RECEIVE_ACK_ERROR REPLY_NOT_ACK TIME_OUT_WAITING_ACK synchronization consideration: synchronized */ public synchronized int rpc_call(rpc_function rf) { //write the function to the output stream if ( rf.to_stream_in_xdr(outbound_bos, version) != rf.OK ) { debug.dumpTrace("client_uip_thread.rpc_call(): send function failed"); if (status == OK) { status = SEND_FUNCTION_FAILED; } return SEND_FUNCTION_FAILED; } //get the reply from the input stream byte[] b = new byte[3]; try { int i0, i1, i2; i0 = outbound_bis.read(); i1 = outbound_bis.read(); i2 = outbound_bis.read(); if ((i0 == -1) || (i1 == -1) || (i2 == -1)) { // end of the stream has been reached debug.dumpTrace("client_uip_thread.rpc_call(): reading ACK error"); if (status == OK) { status = RECEIVE_ACK_ERROR; } return RECEIVE_ACK_ERROR; } b[0] = (new Integer(i0)).byteValue(); b[1] = (new Integer(i1)).byteValue(); b[2] = (new Integer(i2)).byteValue(); } catch (InterruptedIOException e1) { debug.dumpTrace("client_uip_thread.rpc_call(): time out waiting ack"); if (status == OK) { status = TIME_OUT_WAITING_ACK; } return TIME_OUT_WAITING_ACK; } catch (IOException e) { debug.dumpTrace("client_uip_thread.rpc_call(): receive acknowledgement error"); if (status == OK) { status = RECEIVE_ACK_ERROR; } return RECEIVE_ACK_ERROR; } String reply = new String(b); if ( !reply.equals("ACK") ) { debug.dumpTrace("client_uip_thread.rpc_call(): reply not ACK"); return REPLY_NOT_ACK; } return OK; } /** method description: this method do the actural function receiving from server to client uses the services of class(es): rpc_function, debug input parameter(s): none; output parameter(s): none return value: none synchronization consideration: none */ public void run() { while(true) { //get function rpc_function rf = new rpc_function(inbound_bis, version, debug); //function valid check if ( !rf.is_valid() ) { debug.dumpTrace("client_uip_thread.run(): invalid received function"); if (status == OK) { status = ERROR_READ_FUNCTION_FROM_SERVER; } return; } // send back ACK or NAK if ((version == 1) && (rf.get_name() == null)) { // this is an unrecognized function, send back nak byte[] reply = (new String("NAK")).getBytes(); try { inbound_bos.write(reply, 0, 3); inbound_bos.flush(); } catch (Exception e) { debug.dumpTrace("client_uip_thread.run(): send back acknowledgment error"); if (status == OK) { status = ERROR_WRITE_ACK; } return; } } else { // function read out is correct byte[] reply = (new String("ACK")).getBytes(); try { inbound_bos.write(reply, 0, 3); inbound_bos.flush(); } catch (Exception e) { debug.dumpTrace("client_uip_thread.run(): send back acknowledgment error"); if (status == OK) { status = ERROR_WRITE_ACK; } return; } } //check the end session function if ( (rf.get_name() != null) && (rf.get_name().equals("end_session")) ) { status = SERVER_DISCONNECT; return; } cu.process_call_back_from_client_uip_thread(rf); }//end while(1) }//end thread run }//end class client_uip_thread