Avatar billede bentblod Nybegynder
22. januar 2005 - 15:56 Der er 6 kommentarer og
1 løsning

Netværksprogrammering i spil

Hej folkens :) Har lige et lille spørgsmål ang. netværks programmering.
Er godt igang med at lave et lille spil over netværk. Men synes hurtigt at det begynder at lagge i det, så tænkte på om det ville blive bedre hvis man bruger flere porte til at kommunikere med eller hvad der ville være smartest at gøre? Eller skal med hen og kigge på NIO eller hvad ville i forslå?
Avatar billede arne_v Ekspert
22. januar 2005 - 15:59 #1
NIO er meget relevant ved rigtigt mange clients til en server.
Avatar billede arne_v Ekspert
22. januar 2005 - 15:59 #2
Men uden mere info om problemet og din kode er det nok svært at sige noget konkret.
Avatar billede bentblod Nybegynder
22. januar 2005 - 16:06 #3
ok jeg copy paster lige min server og klient så :)
Men har max 8 klienter så er ikke fordi der er RIGTIG mange..

--------------SERVER-------------------


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

public class NetvaerksServer {

  private static ServerSocket servSocket;
  private static final int PORT = 7777;
  private static Vector klienter = new Vector();
  private static BufferedReader in;
  private static PrintWriter out;

  private SpillerTjek[] st = {new SpillerTjek(0,false),
  new SpillerTjek(1,false),
  new SpillerTjek(2,false),
  new SpillerTjek(3,false),
  new SpillerTjek(4,false),
  new SpillerTjek(5,false),
  new SpillerTjek(6,false),
  new SpillerTjek(7,false)
  };


  public NetvaerksServer() {
  }

  /**
  * Metode til at sende en besked om opdateringer til en tank
  * @param option
  * @param tank
  */
  public void sendBeskedTilTraadTank(String option,Tank tank) {
    for (int i = 0; i < klienter.size(); i++) {
      ClientHandler h = (ClientHandler) klienter.elementAt(i);
      h.sendBeskedTank(option,tank);
    }
  }

  /**
  * Metode til at sende en besked om opdateringer til en tank
  * @param option
  * @param tank
  */
  public void sendBeskedTilTraadTankHead(String option,TankHead tankHead) {
    for (int i = 0; i < klienter.size(); i++) {
      ClientHandler h = (ClientHandler) klienter.elementAt(i);
      h.sendBeskedTankHead(option,tankHead);
    }
  }


  /**
  * Metode til at sende en besked om opdateringer til en kanon kugle
  * @param option
  * @param tank
  */
  public void sendBeskedTilTraadCanon(String option,Canonball canonball) {
    for (int i = 0; i < klienter.size(); i++) {
      ClientHandler h = (ClientHandler) klienter.elementAt(i);
      h.sendBeskedCanon(option,canonball);
    }
  }

  public void sendBeskedTilTraadremovecanon(String option, Canonball canonball) {
    for (int i = 0; i < klienter.size(); i++) {
      ClientHandler h = (ClientHandler) klienter.elementAt(i);
      h.sendBeskedRemoveCanon(option, canonball);
    }
  }

  public void sendBeskedTilKollisionSkudTank(String option, Canonball canonball) {
    for (int i = 0; i < klienter.size(); i++) {
      ClientHandler h = (ClientHandler) klienter.elementAt(i);
      h.sendBeskedKollisionSkudTank(option, canonball);
    }
  }


  public void startNetvaerkforbindelsen() throws IOException {
    try {
      servSocket = new ServerSocket(PORT);
      System.out.println("Port er åbnet");
    }
    catch (IOException ex) {
      System.out.println("\nUnable to set up port!");
      System.exit(1);
    }

    do {
      Socket client = servSocket.accept();
      System.out.println("\nNew client accepted.\n");
      for (int i = 0; i < st.length; i++) {
        if (!st[i].isOptaget()) {
          st[i].setOptaget(true);
          ClientHandler handler = new ClientHandler(client,st[i].getSpiller());
          klienter.addElement(handler);
          handler.start();
          System.out.println("Klienten har fået spiller nr: " + st[i].getSpiller());
          System.out.println("0 isAktive: " + st[0].isOptaget());
          System.out.println("1 isAktive: " + st[1].isOptaget());
          System.out.println("2 isAktive: " + st[2].isOptaget());
          System.out.println("3 isAktive: " + st[3].isOptaget());
          System.out.println("4 isAktive: " + st[4].isOptaget());
          System.out.println("5 isAktive: " + st[5].isOptaget());
          System.out.println("6 isAktive: " + st[6].isOptaget());
          System.out.println("7 isAktive: " + st[7].isOptaget());
          break;
        }
      }

    }
    while (true);
  }

  private class ClientHandler
      extends Thread {
    private Socket client;
    private BufferedReader in;
    private PrintWriter out;
    private String besked = "";
    private String option = "";
    private int spiller= 0;
    private int spiller2=0;
    private String player="";


    public ClientHandler() {

    }

    public ClientHandler(Socket socket, int spilleren) {
      client = socket;
      this.spiller2 = spilleren;
      try {
        in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        out = new PrintWriter(client.getOutputStream(), true);
        Tank tank = ServerController.getInstance().getTank(spilleren);
        tank.setAktiveret(true);
        ServerController.getInstance().setTank(tank);
        out.println("Connected");
        out.println(spilleren);
        out.flush();
      }
      catch (IOException ex) {
        ex.printStackTrace();
      }
    }

    /**
    * Run metode som bliver startet af start
    */
    public void run() {
      try {
        do {
          option = in.readLine();
          if (option == null) {
            throw new IOException("End of stream");
          }
          else if (option.equals("left")) {
            ServerController.getInstance().moveLeft(ServerController.getInstance().getTank(spiller2));
          }
          else if (option.equals("right")) {
            ServerController.getInstance().moveRight(ServerController.getInstance().getTank(spiller2));
          }
          else if (option.equals("up")) {
            ServerController.getInstance().moveUp(ServerController.getInstance().getTank(spiller2));
          }
          else if (option.equals("down")) {
            ServerController.getInstance().moveDown(ServerController.getInstance().getTank(spiller2));
          }
          else if(option.equals("tankmove")){

          }
          else if(option.equals("tankheadright")){
            ServerController.getInstance().moveTankHeadRight(spiller2);
          }
          else if(option.equals("tankheadleft")){
            ServerController.getInstance().moveTankHeadLeft(spiller2);
          }
          else if(option.equals("tankshoot")){
            ServerController.getInstance().tankShoot(spiller2);
          }
        }
        while (!besked.equals("QUIT"));
      }
      catch (IOException ex) {
        ex.getStackTrace();
      }

      finally {
        try {
          if (client != null) {
            System.out.println("Closing down connection...");
            st[spiller2].setOptaget(false);
            Tank tank = ServerController.getInstance().getTank(spiller2);
            tank.setAktiveret(false);
            ServerController.getInstance().setTank(tank);
            klienter.removeElement(this);
            client.close();
            System.out.println("size: " + klienter.size());
          }
        }
        catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    }

    public void sendBeskedTank(String option,Tank tank) {
      out.println(option);
      out.println(tank.getSpiller());
      out.println(tank.getAngle());
      out.println(tank.getX());
      out.println(tank.getY());
      out.println(tank.isKoerer());
      out.println(tank.getPic());
      out.println(tank.isAktiveret());
      out.println(tank.getFrontLiv());
      out.println(tank.getRearLiv());
      out.println(tank.getLeftLiv());
      out.println(tank.getRightLiv());
      out.flush();
    }

    public void sendBeskedTankHead(String option,TankHead tankHead) {
      out.println(option);
      out.println(tankHead.getSpiller());
      out.println(tankHead.getAngle());
      out.flush();
    }

    public void sendBeskedCanon(String option,Canonball canonball) {
      out.println(option);
      out.println(canonball.getSpiller());
      out.println(canonball.getX());
      out.println(canonball.getY());
      out.println(canonball.getAngle());
      out.println(canonball.getTid());
      out.flush();
    }

    public void sendBeskedRemoveCanon(String option,Canonball canonball) {
      out.println(option);
      out.println(canonball.getSpiller());
      out.flush();
    }

    public void sendBeskedKollisionSkudTank(String option, Canonball canonball) {
      out.println(option);
      out.println(canonball.getSpiller());
      out.println(canonball.getX());
      out.println(canonball.getY());
      out.flush();
    }

  }

  /**
  * Singleton
  * @return Returnerer den eneste instans af klassen.
  */
  public synchronized static NetvaerksServer getInstance() {
    if (netvaerksServer == null)
      netvaerksServer = new NetvaerksServer();
    return netvaerksServer;
  }

  /**
  * Singleton, indeholder en instans af sig selv, eller null.
  */
  public static NetvaerksServer netvaerksServer = null;

}


-------------KLIENT---------------------

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


public class NetvaerksKlient {
  public NetvaerksKlient() {
  }

  private static InetAddress host;
  private static final int PORT = 7777;
  private static String navn = "";
  public static Socket link;
  private static BufferedReader in;
  private static PrintWriter out;

  public void connect(String host1) {
    String response = "";
    try {
      host = InetAddress.getByName(host1);
      link = new Socket(host, PORT);
      in = new BufferedReader(new InputStreamReader(link.getInputStream()));
      out = new PrintWriter(link.getOutputStream(), true);
      response = in.readLine();
    }
    catch (IOException ex) {
      response = "Not connected";
    }
    System.out.println("" + response);
    if (response.equals("Connected")) {
      try {
        int spiller = Integer.parseInt(in.readLine());
        Controller.getInstance().setSpillerensNr(spiller);
        System.out.println("Klienten har nu fået nr: " + spiller);
      }
      catch (Exception ex) {
        System.err.println("Fejl i at hente spillerens nr: " + ex.getMessage());
      }
      ClientThread ct = new ClientThread(link);
      ct.start();
    }
  }

  public void disconnect() {
    try {
      if (link != null) {
        out.println("QUIT");
        link.close();
        System.out.println("Disconnected");
        System.exit(1);
      }
    }
    catch (IOException ex) {
      ex.printStackTrace();
    }
  }


  public void sendBesked(String option, int spiller) {
    out.println(option);
    out.flush();
  }


  private class ClientThread
      extends Thread {
    private Socket client;
    private BufferedReader in;
    private PrintWriter out;
    private String option = "";

    /**
    * Constructor
    */
    public ClientThread() {

    }
    /**
    * Constructor
    * @param socket
    */
    public ClientThread(Socket socket) {
      client = socket;
      try {
        in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        out = new PrintWriter(client.getOutputStream(), true);
      }
      catch (IOException ex) {
        ex.printStackTrace();
      }
    }

    /**
    * Run metode i tråden, håndtere alle input fra serveren
    */
    public void run() {
      while (true) {
        try {
          String option = in.readLine();
          if(option == null){
            throw new IOException("End of stream");
          }
          else if(option.equals("opdatertank")) {
          int spiller = Integer.parseInt(in.readLine());
          double angle = Double.parseDouble(in.readLine());
          int x = Integer.parseInt(in.readLine());
          int y = Integer.parseInt(in.readLine());
          String koererS = in.readLine();
          int pic = Integer.parseInt(in.readLine());
          boolean koerer = false;
          String aktiveretS = in.readLine();
          boolean aktiveret = false;
          int front = Integer.parseInt(in.readLine());
          int rear = Integer.parseInt(in.readLine());
          int left = Integer.parseInt(in.readLine());
          int right = Integer.parseInt(in.readLine());

          if (koererS.equals("true")) {
            koerer = true;
          }
          else {
            koerer = false;
          }
          if (aktiveretS.equals("true")) {
            aktiveret = true;
          }
          else {
            aktiveret = false;
          }
          Tank tank = new Tank(x,y,angle,spiller,koerer,pic, aktiveret,front,right,left,rear,true);
//          System.out.println("Spiller: " + tank.getSpiller() + ", aktiv: " + tank.isAktiveret());
          Controller.getInstance().opdaterTank(tank);
        }

        else if(option.equals("opdatertankhead")){
          String spillerS = in.readLine();
          String angleS = in.readLine();
          int spiller = Integer.parseInt(spillerS);
          double angle = Double.parseDouble(angleS);
          Tank tank2 = Controller.getInstance().getTank(spiller);
          TankHead tankHead = new TankHead(tank2.getX(),tank2.getY(), angle,spiller);
          Controller.getInstance().opdaterTankHead(tankHead);
        }
        else if(option.equals("opdatercanon")){
          Canonball canonball = new Canonball();
          canonball.setSpiller(Integer.parseInt(in.readLine()));
          canonball.setX(Double.parseDouble(in.readLine()));
          canonball.setY(Double.parseDouble(in.readLine()));
          canonball.setAngle(Double.parseDouble(in.readLine()));
          canonball.setTid(Integer.parseInt(in.readLine()));
          Controller.getInstance().opdaterCanonball(canonball);
        }
        else if(option.equals("removecanonball")){
          Canonball canonball = new Canonball();
          canonball.setSpiller(Integer.parseInt(in.readLine()));
          Controller.getInstance().removeCanonBall(canonball.getSpiller());
        }

        else if (option.equals("kollitionskudtank")) {
          Canonball canonball = new Canonball();
          canonball.setSpiller(Integer.parseInt(in.readLine()));
          canonball.setX(Double.parseDouble(in.readLine()));
          canonball.setY(Double.parseDouble(in.readLine()));
          Controller.getInstance().setKollisionSkudTank(canonball);
        }

      }
      catch (Exception ex) {
        System.err.println("FEJL!!! i netværksklient ! " + ex.getMessage());
        disconnect();
      }
    }
  }
}

  /**
  * Singleton
  * @return Returnerer den eneste instans af klassen.
  */
  public synchronized static NetvaerksKlient getInstance() {
    if (netvaerksKlient == null)
      netvaerksKlient = new NetvaerksKlient();
    return netvaerksKlient;
  }

  /**
  * Singleton, indeholder en instans af sig selv, eller null.
  */
  public static NetvaerksKlient netvaerksKlient = null;

}
Avatar billede dsj Nybegynder
23. januar 2005 - 15:09 #4
En tommelfingerregel hedder, at NIO - afhængigt af hardware - ikke er direkte nødvendigt, før man når grænsen på 2000 samtidige klienter.

Jeg har skrevet en lille artikel om NIO, som beskriver hvilke fordele NIO-teknologien tilbyder server-programmering: http://www.eksperten.dk/artikler/35

Størstedelen af min tid som professionel udvikler går med at udvikle servere, hvoraf flere anvendes i multiplayer-spil. Pt. bygger jeg alle servere på NIO teknologien, da vi taler om store belastninger. Nogle tests jeg har gennemført på min lille, private server viser, at den kan servicere 800 samtidige klienter uden problemer (med forholdsvis højt aktivitetsniveau), men derefter får den problemer med mængden af RAM. Serveren indeholder:

1 GHz AMD Athlon
384 MB SD RAM 66 MHz
Linux Mandrake 9.2

Der er altså tale om en ikke alt for stor maskine - og RAM'en er flaskehalsen. Uden NIO vil jeg tro serveren vil kunne klare mindst 600 samtidige klienter uden problemer, men ikke mere end 800. Hvis der på den samme maskine havde været installeret Windows, vil den helt sikkert kunne klare 800 klienter uden NIO.

Nu ved jeg ikke helt hvor mange samtidige klienter du har på din server, men hvis jeg tror rigtigt, er der tale om maksimalt 100? Hvilket en mindre maskine sagtens kan klare uden NIO. Hvis altså dit spil lagger, er jeg overbevist om, at det skyldes den måde, koden er struktureret på mht. tråde, delte data osv.

Med andre ord: Hvis ikke du forventer flere tusinde brugere, mener jeg ikke, at NIO er nødvendigt.
Avatar billede bentblod Nybegynder
24. januar 2005 - 13:46 #5
Altså nu er det bare et lille spil jeg er igang med at lave, med max 8 klienter og 1 server. Men tror jeg sender for mange informationer over netværket, hvilket får det hele til at lagge, men synes ikke der var nogen der svarede mig på om det ville hjælpe med lag problemet at sende dataen over flere sockets?
Avatar billede dsj Nybegynder
24. januar 2005 - 13:52 #6
Til hver klient har man oftest én åben socket, hvilket også er tilfældet for din server, og nej, det vil ikke performe bedre med flere sockets, medmindre det aflaster en flaskehals i din kode.
Avatar billede bentblod Nybegynder
24. januar 2005 - 14:01 #7
Ok fint nok så. Så må jeg vist bare optimere min kode lidt bedre.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester