Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   Серверные технологии и Flash (http://www.flasher.ru/forum/forumdisplay.php?f=62)
-   -   Java xSocket сервер, зависание. Как исправить? (http://www.flasher.ru/forum/showthread.php?t=135771)

PeTa4eK 01.02.2010 14:02

Java xSocket сервер, зависание. Как исправить?
 
Всем привет. Где-то полторы недели назад, прочитал статью о том как создать простой сервер на Java, подключая к нему Flash.
Вот она: http://giantflyingsaucer.com/blog/?p=205

После этого в порыве восторга, начал делать игру. Делал быстро, попутна изучая способы взаимодействия - оптимизации. За основу был взят сервер из статьи, т.к. я знаю Java, на уровне стандартного языка(тоесть условия, перемнные) написал простенькую проверку на версию флешки(дабы блокировать людей с прошлой версии)

Собственно основной класс сервера:

Код:

package xsocketchatserver;

import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import java.util.*;
import org.xsocket.*;
import org.xsocket.connection.*;


public class xSocketDataHandler implements IDataHandler, IConnectHandler, IDisconnectHandler
{
    private Set<INonBlockingConnection> sessions = Collections.synchronizedSet(new HashSet<INonBlockingConnection>());
           
    public boolean onData(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException
    {
        try
        {
            String data = nbc.readStringByDelimiter("\0");
            // Expected Message Format:
            // USERNAME|MESSAGE
            // ie: JohnDoe~Hello World
           
            if(data.trim().length() > 0)
            { 
              // System.out.println("Incoming data: " + data);
               
                if(data.equalsIgnoreCase("<policy-file-request/>"))
                {
                    nbc.write("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"8090\"/></cross-domain-policy>\0");
                    return true;
                }

                String[] NewData = data.split("~");

                if(NewData[1].equalsIgnoreCase("VERSION")) {
                    String[] FirstData = NewData[0].split("§");

                    if(FirstData[1].equalsIgnoreCase("10")) {
                        sendMessageToAll(nbc, FirstData[0], "YES");
                    } else {
                        sendMessageToAll(nbc, FirstData[0], "NO");
                    }
                    //sendMessageToAll(nbc, NewData[0], NewData[1]);
                } else {
                    sendMessageToAll(nbc, NewData[0], NewData[1]);
                }
             
                //if(message[1].equalsIgnoreCase("SHUTDOWN"))
                  // Main.shutdownServer();
            }
        }
        catch(Exception ex)
        {
            System.out.println("onData: " + ex.getMessage());
        }
       
        return true;
    }
   
    private void sendMessageToAll(INonBlockingConnection nbc, String user, String message)
    {
        try
        {
            synchronized(sessions)
            {
                Iterator<INonBlockingConnection> iter = sessions.iterator();
               
                while(iter.hasNext())
                {
                    INonBlockingConnection nbConn = (INonBlockingConnection) iter.next();
                   
                    if(nbConn.isOpen())
                        nbConn.write(user +"~"+ message + "\0");
                }
            }
           
            //System.out.println("Outgoing data: " + user +"~"+ message);
        }
        catch(Exception ex)
        {
            System.out.println("sendMessageToAll: " + ex.getMessage());
        }           
    }

    public boolean onConnect(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, MaxReadSizeExceededException
    {
        try
        {
            synchronized(sessions)
            {
                sessions.add(nbc);           
            }
           
            System.out.println("onConnect");
        }
        catch(Exception ex)
        {
            System.out.println("onConnect: " + ex.getMessage());
        }
       
        return true;
    }

    public boolean onDisconnect(INonBlockingConnection nbc) throws IOException
    {
        try
        {
            synchronized(sessions)
            {
                sessions.remove(nbc);           
            }
           
            System.out.println("onDisconnect");
        }
        catch(Exception ex)
        {
            System.out.println("onDisconnect: " + ex.getMessage());
        }       
       
        return true;
    }
}


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

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

Может кто помочь с этой проблемой?

При подключении идет отправка трех данных:
Код AS3:

XSocket.send(...код... + "~" + "Connect");
XSocket.send(PlayerName + "~NewPlayerConnect");
XSocket.send("SERVER" + "§" + "<b>" + PlayerName.toString() + "</b>" + " зашел в игру!" + "~CHAT");

Тоесть при подключении серверу отправляются кординаты и после символа ~ команда.
При получении этих данных, сервер отправляет их всем клиентам, там уже выясняется, что где должно создаться, переместиться.

1-я команда говорит тем кто в игре что он подключился, и он появляется у них.
2-я команда говорит что он требует их данных чтобы создать их у себя. После того как сервер отправит эту команду всем клиентам, все клиенты отправят снова запрос на сервер уже о том, что они есть в игре.
3-я команда выводит сообщение в чат, о том что игрок подключился.

1. Из-за чего может быть зависание?
2. Я считаю из-за того что в одну единицу времени приходит много запросов, и сервер не выдержав нагрузки - погибает... Если так, как можно синхронизировать данные с сервером, тоесть чтобы небыло хаотичного принял-отправил? Если никак, просто попытаться уменьшить кол-во запросов? Или вообще не в этом дело?

Заранее спасибо.. :victory:

Dimitry_II 01.02.2010 17:19

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

Если бы ты смог локализовать ошибку (зависание), было бы проще.
Из того, что сразу бросается в глаза - ты пишешь в синхронизированный поток, но не отправляешь (nbConn.flush()) - логично предположить, что пока он не очистится (отправится или перегрузится), с ним ничего не сделаешь - будет в подвешенном состоянии.

Предположение основано на аналогичной работе неблокирующего сервлета. Однако смущает описание функции .write() суперинтерфейса INonBlo... - IConnection: "sends ... to the remote endpoint" - то есть не помещение данных в поток для отправки, а именно отправка данных. Правда, наличие flush успокаивает. Попробуй - вдруг поможет.

PeTa4eK 02.02.2010 12:54

Спасибо, но попробывал.. Не помогло(((

Вот зависание с flush... И последние данные с сервера

Вот пример последних принимаемых данных:
Код:

Incoming data: FDoKENatOR~724.7§29.5§164.9§120 - данные о положении приходят каждые 0.16 сек
Outgoing data: FDoKENatOR~724.7§29.5§164.9§120 - сервер возвращает всем эти данные
Incoming data: FDoKE~593.8§151.6§-135.4§-36.4 - снова положение
Outgoing data: FDoKE~593.8§151.6§-135.4§-36.4 - возвращение положения всем
onConnect - подключение нового игрока
Incoming data: <policy-file-request/> - отправка данных о защите
Incoming data: FDoKENatOR~695.1§34.1§176.9§130.6 - получение данных о положении
onData: channel is closed (read buffer size=0) - кто-то выходит из игры и данные прерываются
Incoming data: FDoKE~572.2§130.3§-135.4§-44.3 - еще одни положения
Incoming data: H.i.M.i.K§90§62.9§24.2§50~SPAWN - данные о спавне в новом месте

Далее полное молчание сервера

До вылета сервер работал 10 минут...

Проанализировав данные я думаю что проблема в отключении игрока. onData: channel is closed (read buffer size=0) так как после нее уже нет ответа на отправленные данные.

Можно это исправить? Или может дело в Incoming data: <policy-file-request/>

Насчет захлебывания и правда я ошибся, т.к. было до 10 человек.. И все было нормально.

Буду благодарен за помощь!

PS: Еще заметил то, что нет строчки onDisconnect которая показывает отключившегося...

Да и еще одна вещь, проанализировал простое подключение - отключение

Код:

onConnect
Incoming data: <policy-file-request/> - при первом подключении флеш ругается на защиту
onData: channel is closed (read buffer size=0) - после игрока отключает
onDisconnect - он отключается
onConnect - и сразу подключается
Incoming data: FDoKE§11~VERSION
Outgoing data: FDoKE~YES
Incoming data: FDoKE~NAMEFREE
Outgoing data: FDoKE~NAMEFREE
Incoming data: FDoKE§661.2§38§48.3§7§50§0~Connect
Outgoing data: FDoKE§661.2§38§48.3§7§50§0~Connect
Incoming data: SERVER§<b>FDoKE</b> зашел в игру!~CHAT
Outgoing data: SERVER§<b>FDoKE</b> зашел в игру!~CHAT
Incoming data: FDoKE~661.2§38§48.3§-7.9
Outgoing data: FDoKE~661.2§38§48.3§-7.9
onData: channel is closed (read buffer size=0)
onDisconnect

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

Добавлено через 1 час 20 минут
Хм немного покапашись и добавив небольшую проверочку, вот:

Код:

onConnect - первое подключение
Incoming data: <policy-file-request/> - требование файла политики
Политика отправлена - проверка что политика отправлена(она отправляется вовремя при 1 игроке, это я проверял)
onData: channel is closed (read buffer size=0) - флеш защита отключает сокет соединение
onDisconnect - отключение(удаление сессие)
onConnect - мгновенное переподключение флешки уже без требования безопасности

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

PeTa4eK 05.02.2010 13:27

Всем спасибо! Проблему решил...

Как решил - создал 2 сервера, изначально флешка подключается к первому, получает файл политики, отключает соединение(чтобы не нагружать этот сервер) и подключается уже к основному, и файл политики уже не требуется! Вот как оказалось все просто ;D

mikhailk 24.06.2010 12:34

PeTa4eK, Вы не сталкивались ли с эксепшном "Too many files"? И если да, то как его локализовали?

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

PeTa4eK 26.06.2010 08:28

Я уже ушел из xSocket, на C#, ибо как-то удобней))
Соединение можно закрыть.

Глянь тутор: http://giantflyingsaucer.com/blog/?p=224
И все поймешь.

mikhailk 26.06.2010 11:57

Тутор этот я видел. ))
Сам сейчас перезжаю на Apache Mina, просто думал, вдруг чего не учел.
Уж больно простой вариант был с xSocket.

Соединения, как я понял, он кэширует у себя внутри.


Часовой пояс GMT +4, время: 03:14.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.