Может ли кто-нибудь помочь мне ускорить мой сетевой код в Java? - PullRequest
0 голосов
/ 18 декабря 2009

Хорошо, я работаю над игрой (вроде даже не над альфой), и она должна быть файтингом 1 на 1, где один человек размещает сервер, а другой подключается. Но сейчас код слишком запаздывает, чтобы что-либо делать. Может кто-нибудь взглянуть на это и сказать мне, как это ускорить? PS: я также использую Slick2D lib.

Сервер:

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

public class SlickServer{
    public static void main(String[] args) throws IOException {

        int MAX_PLAYERS = 3;
        int playerNum = 0;
        Player[] players = new Player[MAX_PLAYERS];
        players[0] = new Player(25,25);
        players[1] = new Player(125,125);
        players[2] = new Player(225,225);
        ServerSocket serverSocket = new ServerSocket(4444);
        boolean listening = true;

        while(listening){
            System.out.println("Waiting to connect with: " + playerNum);
            new ClientThread(serverSocket.accept(), players, playerNum).start();
            //stops here.
            System.out.println("Connected with: " + playerNum + " Now incrementing");
            playerNum++;
            System.out.println("Incremented to: " + playerNum);
        }



        serverSocket.close();
        System.exit(0);
    }
}

Тема сервера:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import java.io.*;

public class ClientThread extends Thread implements Runnable{
    Socket acceptedSocket;
    Player[] players;
    int playerNum;

    public ClientThread(Socket acceptedSocket, Player[] players, int playerNum){
        super("ClientThread");
        this.acceptedSocket = acceptedSocket;
        this.players = players;
        this.playerNum = playerNum;
    }

    public void run(){
        try{

            Socket clientSocket = acceptedSocket;
            System.out.println("Accepted. Now creating I/O.");
            ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
            ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
            System.out.println("I/O with: " + playerNum + " working.");
            out.writeInt(playerNum);
            out.flush();

            while(true){
                if(playerNum == 0){
                    players[0].x = in.readInt();
                    players[0].y = in.readInt();
                    out.writeInt(players[1].x);
                    out.writeInt(players[1].y);
                    out.flush();
                }

                else if(playerNum == 1){
                    players[1].x = in.readInt();
                    players[1].y = in.readInt();
                    out.writeInt(players[0].x);
                    out.writeInt(players[0].y);
                    out.flush();
                }

                else if(playerNum == 2){
                    players[2].x = in.readInt();
                    players[2].y = in.readInt();
                    out.writeInt(players[0].x);
                    out.writeInt(players[0].y);
                    out.flush();
                }
            }

        }

        catch(Exception e){
            e.printStackTrace();
            System.exit(1);
        }


    }


}

Клиент:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

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


public class SlickClient extends BasicGame{

    int MAX_PLAYERS = 3;
    int playerNum = 0;
    Player[] players;
    ClientThread ct;

    int serverDelay = 15;

    public SlickClient()
    {
        super("Client");
    }

    @Override
    public void init(GameContainer gc)
            throws SlickException {
        try{
            players = new Player[MAX_PLAYERS];
            players[0] = new Player(25,25);
            players[1] = new Player(125,125);

            ct = new ClientThread(players);
            ct.start();
            ct.setPriority(Thread.MAX_PRIORITY);

            playerNum = ct.playerNum;   
        }

        catch(Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public void update(GameContainer gc, int delta)
            throws SlickException
    {
        Input input = gc.getInput();

        if(input.isKeyDown(Input.KEY_A))
        {
            players[playerNum].x-=5;
        }

        if(input.isKeyDown(Input.KEY_D))
        {
            players[playerNum].x+=5;
        }

        if(input.isKeyDown(Input.KEY_W))
        {
            players[playerNum].y-=5;
        }

        if(input.isKeyDown(Input.KEY_S))
        {
            players[playerNum].y+=5;
        }



    }

    public void render(GameContainer gc, Graphics g)
            throws SlickException
    {
        g.fillRect(players[0].x, players[0].y, 50, 50);
        g.fillRect(players[1].x, players[1].y, 50, 50);

    }

    public static void main(String[] args)
            throws SlickException
    {
         AppGameContainer app =
            new AppGameContainer( new SlickClient() );

         app.setAlwaysRender(true);
         app.setTargetFrameRate(30);
         app.setDisplayMode(800, 600, false);
         app.start();
    }
}

class ClientThread extends Thread implements Runnable{
    Socket socket;
    Player[] players;
    int playerNum;
    ObjectOutputStream out;
    ObjectInputStream in;

    public ClientThread(Player[] players){
        super("ClientThread");

        try{

            socket = new Socket("98.203.176.196", 4444);
            out = new ObjectOutputStream(socket.getOutputStream());
            in = new ObjectInputStream(socket.getInputStream());
            this.players = players;
            playerNum = in.readInt();
            System.out.println(playerNum);

        }

        catch(Exception e){
            e.printStackTrace();
        }
    }

    public void run(){
        try{
            while(true){
                if(playerNum == 0){
                    try{
                        out.writeInt(players[0].x);
                        out.writeInt(players[0].y);
                        out.flush();
                        players[1].x = in.readInt();
                        players[1].y = in.readInt();
                    }

                    catch(Exception e){
                        e.printStackTrace();
                    }
                }

                else if(playerNum == 1){
                    try{
                        out.writeInt(players[1].x);
                        out.writeInt(players[1].y);
                        out.flush();
                        players[0].x = in.readInt();
                        players[0].y = in.readInt();
                    }

                    catch(Exception e){
                        e.printStackTrace();
                    }   
                }   
            }
        }

        catch(Exception e){
            e.printStackTrace();
        }
    }
}

Ответы [ 6 ]

2 голосов
/ 18 декабря 2009

Это только лагает, когда клиент находится на удаленной машине с сервера? Если так, и у вас будет хорошее время для проверки связи между блоками, тогда я сделаю дикое предположение и скажу, что вы, возможно, сталкиваетесь с алгоритмом Нейгла здесь? Данные, которые вы отправляете, очень малы (writeInt).

Вы можете попробовать использовать посредника BufferedOutputStream, хотя, если вы хотите попробовать быстрое исправление, вы можете просто попробовать отключить Nagle на своих сокетах.

Socket.setTcpNoDelay (истина);

1 голос
/ 18 декабря 2009

Если хотите, можете попробовать использовать сокеты датаграмм. Если базовый пакет отбрасывается, он просто исчезает, но, поскольку вы просто обновляете позицию, все должно быть в порядке.

1 голос
/ 18 декабря 2009

Зависит от того, почему это медленно. Вот пара предложений

  1. Используйте только неблокирующий ввод / вывод. Как специализация этого правила, никогда не используйте RPC.
  2. Рассмотрите возможность прогнозирования на стороне клиента, где это необходимо Предсказание на стороне клиента предполагает, что то, что хочет сделать клиент, будет в порядке, и опирается на клиента, как если бы это было нормально. Большинство, если не все сетевые игры используют это.
  3. Примите, что у вас нет «синхронизированного» состояния между вашими игроками в игре в реальном времени. Такого просто не будет.

Кроме того, определите, что вы подразумеваете под "становится запаздывающим". Вы имеете в виду, что код дает плохие частоты кадров? Что для регистрации ходов требуется много времени? Выяснение этого станет первым шагом к оптимизации.

И удачи. Получение боевой игры, способной реагировать по сети, будет непростой задачей даже для профессиональных разработчиков, имеющих опыт программирования сетевых игр. Возможно, вам придется изменить дизайн игры до такой степени, чтобы скрыть отставание - некоторые вещи просто невозможны в сети.

1 голос
/ 18 декабря 2009

Хм, несколько вещей, которые вы можете попробовать прямо сейчас, но для того, чтобы делать то, что вы хотите, потребуется некоторая работа.

Во-первых, и ваш клиент, и ваш сервер находятся в очень узких циклах. Обычно цикл, который вы используете, должен спать в течение минимального количества времени на каждом проходе, чтобы цикл не потреблял слишком много ресурсов процессора, ничего не делая.

Во-вторых, вам, вероятно, следует использовать неблокирующий ввод / вывод (проверьте входные данные от каждого клиента, прежде чем пытаться его прочитать).

Наконец, если вы не хотите приступить к обучению, касающемуся сетей Java, или если у вас нет конкретных задач, требующих нестандартного протокола связи, вы можете рассмотреть возможность использования серверной инфраструктуры, которую вы можете просто расширить. Простой способ сделать это - создать серверную часть с помощью Jetty или Tomcat и использовать Apache HTTPClient для своей клиентской стороны.

Если вы решили остаться с выделенным сервером, простые серверные инфраструктуры в книге Core Java или книге по сетевым технологиям Java (O'Reilly) дадут вам лучшую базу.

Надеюсь, это поможет.

1 голос
/ 18 декабря 2009

Ну, код, который вы предоставляете, неполон ... Я собирался сделать это, а затем покажу вам, как это сделать, чтобы я мог быть уверен в том, что вы увидите ... однако мы будем работать с тем, что имеем.

1) Запустите его на одной машине как для клиента, так и для сервера - это все еще медленно? 2) Пинг между машинами и посмотреть, как скорость 3) профиль приложения.

# 1 помогает определить, является ли это проблемой с сетью. И клиент, и сервер на одном компьютере исключают сеть.

# 2 помогает определить, является ли это проблемой с сетью. Если пинг плохой, то игра будет медленной.

# 3 сложнее сделать, но даст вам результаты, где в вашем коде он медленный.

Для профиля взгляните на Visual VM (или, если вы используете netbeans, просто профилируйте его там ... eclipse, вероятно, тоже что-то имеет, возможно, в качестве плагина).

Для профилирования я хотел бы начать с сервера и посмотреть, что там происходит медленно, а затем перейти к клиенту. Я предлагаю это только потому, что на сервере происходит меньше, поэтому должно быть проще увидеть, что происходит.

1 голос
/ 18 декабря 2009

Вы пытались использовать что-то кроме ObjectOutputStream и ObjectInputStream для отправки и получения данных? Их использование включает в себя сериализацию и десериализацию объектов, и кажется (хотя бы догадкой), что использование DataOutputStream может быть быстрее.

...