2

我有一个 Java 客户端,它每 33 毫秒不断更新我的游戏程序中的一个类。问题是当我打开两个接收相同类并且应该同步的客户端时,它们会根据谁首先开始或与服务器联系而产生不同的结果。然后在值永远不会改变但我(现在)知道服务器正在向两者发送写入类之后。在我的导师讲义中,它说要这样做

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

这就是我被教导的方式,但现在我来实施它(在大学不需要这个)它不起作用。我花了数周和数月的时间试图找出原因,今天我认为我破解了原因,但不知道如何解决它。基本上从开始 input.flush(); ObjectInput 或 ObjectInputStream 似乎不存在,因此我将其排除在外并对其进行了测试,并且使用断点验证了我的服务器类已发送并设置了我的游戏设置类的所有变量,我的类显示得很可爱。问题出现在服务器上此类的下一次更新中,但我花了很长时间才解决。我相信正在发生的事情(如果我不放弃的话)是它读取发送的第一个对象并将其保存在输入流中。然后,当我调用相同的方法时,它会不断地给我第一个对象。我假设这是因为我没有像我的导师在讲座幻灯片中所说的那样冲洗它。输出流有一个我使用的冲洗。我还查看了 ObjectInput 和 ObjectInputStream ,并且无法看到刷新或替代方法或移动到流中下一个对象的方法。无论如何,我认为后者不是一个好主意,因为流只会持续增长,直到我假设内存可能耗尽。无论如何继承我的接收类和我的服务器。:无论如何,我认为后者不是一个好主意,因为流只会持续增长,直到我假设内存可能耗尽。无论如何继承我的接收类和我的服务器。:无论如何,我认为后者不是一个好主意,因为流只会持续增长,直到我假设内存可能耗尽。无论如何继承我的接收类和我的服务器。:

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);
       }
   }
}

该行:BaseSerialDataObjects bSO = (BaseSerialDataObjects) input.readObject();

是我在下面的返回 bSO 行上放置断点的地方,它总是显示一个与第一个具有相同值的类,即使我只是在服务器上放置断点并在返回使客户端的类之前检查了它的不同值在我检查它时挂起,这样我就知道它是正确的线程和类以及所有内容

服务器 :

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--;
    }
}

提前感谢约翰哈里斯

4

2 回答 2

1

除此之外flush,您还需要reset(在ObjectOutputStream中)。对象流,读取和写入,保存它们读取和写入的所有内容。两个问题:一,你两边的内存都用完了。二、如果两次发送同一个对象,读取的对象会包含第一次读取的数据。第二次,写入只是发送对象 ID(本质上),而不是数据。每次呼叫后呼叫重置writeObject

于 2013-07-10T20:14:23.810 回答
1

您不应该在每个线程中重用相同的静态套接字引用服务器。

您想使用每次接受时创建的新套接字:

Socket newSocket = server.accept();

并将其传递给每个线程:

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

然后在 TCPserver 内部,仅使用对newSocket的引用。

为了强迫你这样做,去掉这个声明:

 static Socket server = null;  

并在您的主要方法中将其设为局部变量:

 Socket server = new ServerSocket(5000);

这是我的一个程序中的服务器:

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);
        }
    }
}

它使用线程池,而不是每次都创建一个新的thead,但基本思想是一样的。我的“ConnectionHandler”类相当于你的“TCPserver”类。

于 2013-07-10T17:08:42.833 回答