Тонкая настройка игрового сервера для сокетов Java - PullRequest
1 голос
/ 26 августа 2011

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

  1. Я хотел бы знать, есть ли лучший способ анализа запросов вместо разделения строк ввода (,) запятыми, как я делаю.

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

  3. А что касается безопасности потока, все ли в порядке с Collections.synchronizedList и синхронизированными блоками на итерациях? Или есть способ лучше / чище?

  4. Наконец, есть ли библиотека java sockes, которая будет делать все эти вещи выше? Если это так, я должен использовать его или это излишество для маленького игрового сервера Java-сокетов.

  5. Что-нибудь еще, что я должен улучшить?

Спасибо:)

Ниже приведена базовая структура моего сервера.

public class Servidor extends Thread {

    private ServerSocket serverSocket;
    public static boolean LISTENING = true;
    private final List<Client> clients = Collections.synchronizedList(new ArrayList<Client>());
    private final List<Game> games = Collections.synchronizedList(new ArrayList<Game>());

    public Servidor() {
        try {
            serverSocket = new ServerSocket(SERVER_PORT);
        } catch (IOException e) {
            Log.add("error starting server: " + e);
        }
    }

    @Override
    public void run() {

        // Wait for players to connect
        while (LISTENING) {
            try {
                Client c = new Client(serverSocket.accept());
                clients.add(c);
                c.start();
            } catch (IOException e) {}
        }
    }



    class Client extends Thread {

        private Socket socket;
        private PrintWriter out;
        private BufferedReader in;
        private boolean loggedin;
        private Player player;
        private Game game;


        public Cliente(Socket sock) {
            socket = sock;
            try {
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8")), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
            } catch (IOException e) {
                Log.add("error connecting to player!");
            }
        }


        /* Send to this player */
        public void send(String s) {
            out.println(s);
        }

        /* Send to all players on the server */
        public void boardcast(String s) {
            synchronized (clients) {
                for (Client c : clients) {
                    c.send(s);
                }
            }
        }

        /* Get player by ID */
        private Player getPlayerByID(int playerID) {
            synchronized (clients) {
                for (Client c : clients) {
                    if (c.player != null) {
                        if (c.player.getID() == playerID) {
                            return c.player;
                        }
                    }
                }
            }
            return null;
        }

        /* Split inputLine */
        private String[] splitInput(String input, int argsNumber) {
            String[] args = null;

            try {
                args = input.split(",", -1);

                if (args.length != argsNumber) {
                    args = null;
                }
            } catch (Exception ex) {
                Log.add("error splitting input");
            } finally {
                return args;
            }
        }

        @Override
        public void run() {
            char cmd;
            String inputLine, outputLine;
            String[] args;

            try {
                loop:
                while ((inputLine = in.readLine()) != null) {

                    // check if inputLine have 2 chars (CMD_TYPE:)
                    if (inputLine.length() < 2) {
                        kickPlayer();
                        break loop;
                    }

                    // get CMD
                    cmd = inputLine.charAt(0);

                    // remove (CMD_TYPE:) from inputLine
                    inputLine = inputLine.substring(2);

                    // check e player is logged (L:username,password)
                    if (!loggedin) {

                        // check if the input string have 2 arguments
                        if ((args = splitInput(inputLine, 2)) == null) {
                            kickPlayer();
                            break loop;
                        }else{

                            // ... TESTE LOGIN ON DATABASE ...

                            // set player data
                            player = new Player(query.getInt("player_id"), query.getString("username"), query.getInt("level"))

                        }

                    }else{
                        // Commands
                        switch (cmd) {

                            // P:CARD_ID,TARGET_ID eg:(P:5:3)
                            case CMD_PLAY: 

                                // check if the input string have 2 arguments
                                if ((args = splitInput(inputLine, 2)) == null) {
                                    kickPlayer();
                                    break loop;
                                } else {

                                    // ... VALIDATE OTHER PARAMETERS ...

                                    // update game
                                    game.addCard(args[0], args[0]);

                                    // update players
                                    boardcast(CMD_PLAY + ":" + player.getID+ "," + game.LastCard());

                                }

                                break;

                                // ... TEST OTHER COMMANDS ...

                            default:
                                Log.add("invalid command";
                                break loop;
                        }
                    }
                }

            } catch (IOException e) {
                    Log.add("connection lost";
            } finally {
                    removeClient();
            }
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 26 августа 2011

Я программист уже 3 года, но сейчас я действительно пытаюсь освоить программирование сетевого сервера.

Несколько советов, которые могут вам помочь:

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

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

Это было бы что-то вроде:

class ClientHandler
{
   public void run()
   {
      while(isConnected())
         messageRouter.handleMessage(getNextMessage());
   }
}

interface MessageHandler
{
   public boolean canHandle(Message m);
   public void handleMessage(Message m);
}


class MessageRouter
{
   private List<MessageHandler> handlers;

   public void handleMessage(Message msg)
   {
      for(MessageHandler m : handlers)
      {
         if(m.canHandle(msg))
         {
            m.handle(msg);
            return;
         }
      }
      throw UnsupportedMessage();
   }
}

Нечто подобное ...

Кстати: мне слишком бразилец, удачи в вашем проекте

0 голосов
/ 26 августа 2011

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

Ссылка на сокеты Java ssl находится здесь здесь

Ссылку для создания собственного хранилища ключей можно найти здесь

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

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

Что касается анализа строки, то, насколько я знаю, объекты могут передаваться удаленно, поэтому вы можете передавать массив строк вместо того, чтобы анализировать их. Я лично разбил свое общение на несколько вызовов для чтения и записи во входные потоки (вероятно, самый медленный подход :))

и всем путям удачи в вашем проекте. Напишите мне, если вам нужен источник :) .

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