john
john

Reputation: 157

java serialization socket inputstream flush

I have a java client that is constantly every 33 ms updating a class in my game program. the problem is when i open two clients that receive the same class and should be synced they have different results depending on who started or made contact with the server first. Then after the values never change but i (now) know the server is sending out the write class to both. In my tutors lecture notes it says to do it like this

ObjectInput input;
input  = new ObjectInputStream(socket.getInputStream());
serializableobject = (cast) input.readObject();
input.flush();

which is how i was taught but now ive come to implement it (didnt need this at uni) it doesnt work. Ive spent literraly weeks and months trying to work out why and today think i cracked why but dont know how to fix it. Bassically from the start input.flush(); doesnt seem to exist for ObjectInput or ObjectInputStream so I left it out and tested it and my class was displayed lovely using break point to verify my server class was sent and set all the variables of my games settings class. the problems come on the next update of this class from the server but its taken me ages to work out. What i believe is happening (if not im gunna give up)is that it reads the very first object sent and holds it in the input stream. Then when i call the same method it continually just gives me the 1st object back again. Im assuming this is because i dont flush it like my tutor says in the lecture slides. The outputstream has a flush which i use. I also have looked in both ObjectInput and ObjectInputStream and wasent able to see flush or an alternative or a way of moving on to the next object in the stream. the latter i dont think would be a good idea anyway becuase the stream will just continual grow until possibly running out of memory i assume. anyway heres my receiving class and my server.:

import java.io.*;
import java.net.*;
import java.util.*;

public class BaseServer 
{
        private String result = null;

        //for serializable class input stream
        ObjectInput input;


     // Declare client socket
        Socket clientSocket = null;         

     // Declare output stream and string to send to server 
        DataOutputStream os = null;


     // Declare input stream from server and string to store input received from server
        BufferedReader is = null;
        String responseLine;

    //get ip
        String serverAddress ;                                    

    // Create a socket on port 5000 and open input and output streams on that socket
    public void  setUpNetwork(String serverAd)
    {          
        try
        {
            serverAddress = serverAd;
            clientSocket = new Socket(serverAddress, 5000);

            //string
            os = new DataOutputStream(clientSocket.getOutputStream());          
            is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

            //serial
            input = new ObjectInputStream( clientSocket.getInputStream() );

        } 
        catch (UnknownHostException e)
        {
            System.err.println("Don't know about host: hostname");
        }
        catch (IOException e)
        {
            System.err.println("Couldn't get I/O for the connection to: hostname");
        }      
    }

    /*
     * Used to communicate with server
     * takes message to send and the object expecting the response
     * 1 simple method to replace all but one of the below v1 methods
     */
    public BaseSerialDataObjects serverTalk(String message){
        sendStringMessage(message);

        try {   
            BaseSerialDataObjects bSO =  (BaseSerialDataObjects) input.readObject();            

            return  bSO;
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }


    ////////Old v.1 methods for interacting with servers. I have now changed my mind
    //and am using strings to send messages and serializable objects as return messages 
    //I have left these in as I have plans to reuse the most of the code in this game

    public String read(){
          // Write data to the socket
          if (clientSocket != null && os != null && is != null)
            {
             try
                {                           
                    os.writeBytes("ok \n");

                    return is.readLine(); // my bit   
                }
                catch (UnknownHostException e)
                {
                    System.err.println("Trying to connect to unknown host: " + e);
                }
                catch (IOException e)
                {
                    System.err.println("IOException:  " + e);
             }
          }
        return responseLine;
       }          

    //Sends messages to the server
    public void sendStringMessage(String message){
        // Write data to the socket
        if (clientSocket != null && os != null && is != null){
            try {                        
                os.writeBytes( message + "\n" );                                 
                }
            catch (UnknownHostException e){
                System.err.println("Trying to connect to unknown host: " + e);
                }
            catch (IOException e) {
                System.err.println("IOException:  " + e);
                }
            }   
        }          

    /*
     * V.1 method idea am leaving in for an option in future games
     */
    public  String getStringResult(String returnMessage)
   {    
       String tempHolder = read();
       if(tempHolder!=null)
       {
       String[] array = tempHolder.split("\\s+");
       if(array[0].trim().equalsIgnoreCase(returnMessage))
       {
           result = null;      
           tempHolder = "";

          for(int i = 1; i < array.length;i++)
          {           
              tempHolder = tempHolder +  array[i] + " ";        
          }
        return tempHolder.trim();
       }
       else return null;
       }
       else
         {
           return null;
           }

   }   

    public synchronized void setResult(String res)
    {
       result = res; 
    }    

    public void closeConnections()
   {
    // Close the input/output streams and socket
       try{
        os.close();
        is.close();
        clientSocket.close(); 
       }catch(Exception e)
       {
           System.err.println("Exception: " + e);
       }
   }
}

the line: BaseSerialDataObjects bSO = (BaseSerialDataObjects) input.readObject();

is the one where i put a breakpoint on the return bSO line below and it always shows a class with the same values as the first even though ive just put breakpoint on server and checked its got different values just before returning the class which makes the client hang while i inspect it so i know its the right thread and class and everything

Server :

import java.io.*;
import java.net.*;
import java.*;
import java.util.regex.*;

public class TCPserver implements Runnable 
{
    static Socket server = null;    

    private static final int possibleNumberOfPlayers = 8;
    private static final int amountOfPlayerInfoHeld = 6;
    private int threadNumber;

    private static ServerSettings gameSettings = new ServerSettings();
    private static int numberOfSettings = 4;

    //position 0 = name;
    //position 1 = angle;
    //position 2 = position x
    //position 3 = position y
    //position 4 = state

    //position 5 = state in relation to server
    //POssible states: 
        // connected
    static PlayerPositionsSerial positions = new PlayerPositionsSerial();

    public static void main( String args[] )
    {
       positions.playersArray = new String [possibleNumberOfPlayers][amountOfPlayerInfoHeld];

      // Declare a server socket and a client socket for the server
      ServerSocket service = null;

      // Try to open a server socket on port 5000
      try
       {
         service = new ServerSocket(5000);
            server = service.accept();
            Thread t0 = new Thread (new TCPserver(0));
           t0.start();
            server = service.accept();
            Thread t1 = new Thread (new TCPserver(1));
           t1.start();
            server = service.accept();
            Thread t2 = new Thread (new TCPserver(2));
           t2.start();      
            server = service.accept();
            Thread t3 = new Thread (new TCPserver(3));
           t3.start();
            server = service.accept();
            Thread t4 = new Thread (new TCPserver(4));
           t4.start();          
            server = service.accept();
            Thread t5 = new Thread (new TCPserver(5));
           t5.start();
            server = service.accept();
            Thread t6 = new Thread (new TCPserver(6));
           t6.start();          
            server = service.accept();
            Thread t7 = new Thread (new TCPserver(7));
           t7.start();
            /*server = service.accept();
            Thread t8 = new Thread (new TCPserver(8));
           t8.start();  */      


      }
      catch (IOException e)
       {
         System.out.println(e + "Error B");
      }        


        }

    public void run()
    {
    // Declare an input stream and String to store message from client      
        BufferedReader is;
        String line;

        // Declare an output stream to client       
       DataOutputStream os;

        String thr = Integer.toString(threadNumber);

      // Create a socket object from the ServerSocket to listen and accept
      // connections. Open input and output streams
      try
       {
            ObjectOutput output;
            output = new ObjectOutputStream( server.getOutputStream() );

            is = new BufferedReader( new InputStreamReader(
                                         server.getInputStream()));



           //if( (line = is.readLine()) != null )
            while( (line = is.readLine()) != null )
            {
                if(line != null)
                {                   
                    switch(rules(line)){
                    case "playertable": 
                        output.writeObject( positions );    

                        break;
                    case "gamesettings": 
                        output.writeObject( gameSettings );
                        break;
                    case "servernumber": 
                        StringReturnSerial string = new StringReturnSerial();
                        string.s = Integer.toString(threadNumber);
                        output.writeObject(string);
                        break;
                        default:
                            System.out.println("line didnt select anything");
                            break;
                    }
                }output.flush();

            }

            // Comment out/remove the stream and socket closes if server is to remain live. 

         is.close();

      }  
      catch (IOException e)
       {
         System.out.println(e + "Error B");         
      }         
    }

    public TCPserver(int tNumber)
    {
        threadNumber = tNumber;
    }

    private synchronized void changeArray(int row, int col, String value)
    {
        positions.playersArray[row][col]  = value;
    }

    private synchronized String readArray(int row, int col)
    {
        return positions.playersArray[row][col];
    }   

    private String rules(String lineIn)
    {
        try {
             String[] splitArray = lineIn.split("\\s+");

                 switch(splitArray[0])
                 {
                    case "SIGNIN":

                        positions.playersArray[threadNumber][0] = splitArray[1];
                        positions.playersArray[threadNumber][4] = "normal";
                        positions.playersArray[threadNumber][amountOfPlayerInfoHeld-1] = "connected";                       
                        addPlayer();
                        gameSettings.gameStartTime = System.currentTimeMillis() + 10000;

                        return "gamesettings";

                    case "ok":
                    // just for reply, do nothing response heard "ok"
                        break;
                    case "MATCHMAKE":
                            positions.playersArray[threadNumber][amountOfPlayerInfoHeld -1] = "matchmake";
                            gameSettings.gameState = "matchmake";
                            return "playertable";

                    case "READY":

                        positions.playersArray[threadNumber][amountOfPlayerInfoHeld -1] = "ready";

                        return "gamesettings";


                    case "REQUESTSTART":

                          boolean goAhead = true;
                            for(int i = 0 ; i < gameSettings.numberOfConnectedPlayers; i++)
                            {
                                if(positions.playersArray[i][amountOfPlayerInfoHeld-1] != "ready")
                                {
                                    goAhead = false;
                                }
                            }                       

                            if(goAhead)
                            {
                                long start = System.currentTimeMillis( );
                                start = start + 10000;
                                gameSettings.gameStartTime = start;
                            }

                            return "gamesettings";

                        case "GETPOS":

                            return "playertable";                           

                        case "UPDATEPOS":

                            //heres where to notice crashes and check for wins etc...
                            positions.playersArray[threadNumber][1] = splitArray[1];
                            positions.playersArray[threadNumber][2] = splitArray[2];
                            positions.playersArray[threadNumber][3] = splitArray[3];
                            positions.playersArray[threadNumber][4] = splitArray[4];
                            return "playertable";

                        case "GETSETTINGS":
                            return "gamesettings";

                        /*case "SENDSETTINGS":

                            // updates settings 
                            for (int i = 1; i < splitArray.length; i++){
                                switch(i){
                                case 1: 
                                    gameSettings.gameState = splitArray[i];
                                    break;
                                case 2: 
                                    gameSettings.winningNumberOfLaps = Integer.parseInt(splitArray[i]);
                                    break;
                                case 3: 
                                    gameSettings.winString = splitArray[i];
                                    break;
                                case 4: 
                                    gameSettings.gameStartTime = Long.parseLong(splitArray[i]);
                                    break;
                                case 5: 
                                    gameSettings.numberOfConnectedPlayers = Integer.parseInt(splitArray[i]);
                                    break;
                                }
                            }

                            returnString = "gamesettings";
                            break;
                            */

                        case "GETSERVERNUMBER":
                            return "servernumber";                          

                        case "PLAYING":
                            gameSettings.gameState = "playing";

                            break;
                        case "SERVERPLAYERSTATEUPDATE":

                            int crashed = 0;
                            positions.playersArray[Integer.parseInt(splitArray[1])][5] = splitArray[2];
                            for(int i  = 0; i < gameSettings.numberOfConnectedPlayers;){

                                //takes into account possibility of people leaving game
                                if(positions.playersArray[i][5] != null){
                                    if(positions.playersArray[i][5] == "explode"){
                                        crashed++;
                                    }
                                }
                            }
                            if(crashed == gameSettings.numberOfConnectedPlayers)
                                gameSettings.gameState = "explode";

                            return "gamesettings";  

                        default:
                            System.err.println("Rule Not Found: " + splitArray[0]);
                            break;

                 }

        } catch (PatternSyntaxException ex) {

                System.err.println("error C: " + ex);
        }

        return null;
    }

    public synchronized void addPlayer()
    {
        gameSettings.numberOfConnectedPlayers++;
    }

    public synchronized int getNumberOfPlayers()
    {
        return gameSettings.numberOfConnectedPlayers;
    }

    public synchronized void removePlayer()
    {
        gameSettings.numberOfConnectedPlayers--;
    }
}

thanks in advance JOhn harris

Upvotes: 2

Views: 7212

Answers (2)

RalphChapin
RalphChapin

Reputation: 3158

In addition to flush, you need reset (in ObjectOutputStream). Object streams, read and write, save everything they read and write. Two problems: One, you run out of memory on both sides. Two, if you send the same object twice, the object read will contain the data of the first read. The second time, the write just sends the object ID (essentially), not the data. Call reset after each call to writeObject.

Upvotes: 1

Keith
Keith

Reputation: 4184

You shouldn't be reusing the same static socket reference server in each thread.

You want to use the new socket created upon each accept:

Socket newSocket = server.accept();

and pass that to each thread:

Thread t3 = new Thread (new TCPserver(3, newSocket));

Inside of TCPserver then, use ONLY the reference to newSocket.

To force you to do this, get rid of this declaration:

 static Socket server = null;  

and just make it a local variable in your main method:

 Socket server = new ServerSocket(5000);

Here is a server from one of my programs:

private ExecutorService executorService = Executors.newFixedThreadPool(10);

private void acceptConnections() {
    listening = true;
    while (listening) {
        try {
            final Socket connection = serverSocket.accept();
            System.err.println("SERVER - connection from: " + connection.getInetAddress());
            executorService.execute(new ConnectionHandler(connection));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

It uses a thread pool, instead of creating a new thead each time, but the basic idea is the same. My "ConnectionHandler" class is equivalent to your "TCPserver" class.

Upvotes: 1

Related Questions