Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Сообщения за день
 

Вернуться   Форум Flasher.ru > Flash > ActionScript 3.0

Версия для печати  Отправить по электронной почте    « Предыдущая тема | Следующая тема »  
Опции темы Опции просмотра
 
Создать новую тему Ответ
Старый 12.02.2012, 18:07
VInchensoo вне форума Посмотреть профиль Отправить личное сообщение для VInchensoo Найти все сообщения от VInchensoo
  № 1  
Ответить с цитированием
VInchensoo

Регистрация: Jan 2012
Сообщений: 9
По умолчанию Данные по сети приходят с большим опозданием(Socket: as3.0 + java)

Добрый день. Не так давно работаю на actionscripts3.0, занялся созданием клиент-серверного взаимодействия и столкнулся с проблемой:

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

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

На сервер все уходит на ура, обратно возникают проблемы.

Коды сервера и клиента прилагаются. Есть идеи, как решить? XML и фреймворки не хотелось бы.

Насчет размера пакета не уверен, учитывается ли байт размера, писал и 12, и 13- трассировщиком ничего отловить внятного не смог. Из моих наблюдений:

1. Пакет всегда рвется на разные части, определяется каким-то великим рандомом.
2. Функция sData(см. код) вызывается ровно 2 раза(если 3 порции информации, то в третий раз вызова нет и обработка не происходит).

Коды:

AS3.0:

Код AS1/AS2:
package {
	import flash.display.Sprite;
	import flash.net.Socket;
	import mx.core.BitmapAsset;
	import flash.events.*;
 
	public class Main extends Sprite {
 
		[Embed(source = 'load11.jpg')] private var load11:Class;
 
 
		public var buff: int;
		public function Main():void {
 
			var load11:BitmapAsset = new load11();
			var spr:Sprite = new Sprite();
			spr.addChild(load11);
 
			addChild(spr);
 
 
 
 
			spr.addEventListener(MouseEvent.CLICK, func);
 
 
 
			function func(event:MouseEvent):void
			{
				trace("use socket");
 
 
				var s: Socket = new Socket();
				s. addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
 
				  function ioErrorHandler(event:IOErrorEvent):void {
					trace("ioErrorHandler: " + event);
																			}
 
				s.connect("localhost", 1011);
				var str: String;
				str = 'Test';
 
 
			s.writeByte(10); s.writeUTF(str); s.writeByte(15); s.writeUTF(str); s.flush(); buff = -1;
 
 
				s.addEventListener(ProgressEvent.SOCKET_DATA, sData);
 
				function sData(event:ProgressEvent):void
				{
 
 
					if (buff == -1) { buff = s.readByte(); }
 
					if (buff == event.bytesLoaded)
					{
 
					var x:int, y:int, l:int;
					var z:String;
					var z1: String;
 
					x = s.readByte();
					trace(x);
 
					y = s.readByte();
					trace(y);				
 
					z = s.readUTF();
					trace(z);
 
 
					} else { trace("not ready: " + event.bytesLoaded + " " + buff ); }
 
				}
			}
 
 
		}
	}
}
JAVA:

Код:
/* ChatServer.java */
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

class Server {
    private static int port = 1011; /* port the server listens on */

    public static void main (String[] args) throws IOException {
        ServerSocket server = null;
        try {
            server = new ServerSocket(port); /* start listening on the port */
        } catch (IOException e) {
            System.err.println("Could not listen on port: " + port);
            System.err.println(e);
            System.exit(1);
        }

        Socket client = null;
        try {
            client = server.accept();
        } catch (IOException e) {
            System.err.println("Accept failed.");
            System.err.println(e);
            System.exit(1);
        }


        DataInputStream in = new DataInputStream(client.getInputStream());
        
        DataOutputStream os = new DataOutputStream(client.getOutputStream());
        
        System.out.println("I Live!!!");
        
 
     

        byte msg, def;
        String str; 
        /* loop reading lines from the client and display them */
        
        msg= in.readByte();
        
        if (msg!=-1){  	str= in.readUTF();
        				def=in.readByte();
        				str= in.readUTF();
        				System.out.println("Client says: " + msg + " "+ str + " : " + def); 
        			 	msg= (byte) (msg+17);
        				def= (byte) (def *2);
           				str="test";
           		
      
           				
           				os.writeByte(12);//Размер пакета
           				
           				os.writeByte(msg);
           				
        				os.writeByte(def);
        				
        				os.writeUTF(str);		
        		
        			
        				os.flush();
        				
        				}
        
    }
}


Последний раз редактировалось VInchensoo; 12.02.2012 в 18:38.
Старый 13.02.2012, 01:27
maxkar вне форума Посмотреть профиль Отправить личное сообщение для maxkar Найти все сообщения от maxkar
  № 2  
Ответить с цитированием
maxkar

Регистрация: Nov 2010
Сообщений: 497
Никто не гарантирует, что даныне придут "пакетом". И в протоколе обмена нет понятия "порция" данных. Причин, по которым могут изменяться "размеры" получаемого блока данных - масса. Поэтому нужно правильно разбирать поток.
Цитата:
Функция sData(см. код) вызывается ровно 2 раза(если 3 порции информации, то в третий раз вызова нет и обработка не происходит).
Ну так может все данные уже пришли при втором вызове функции, откуда он третий раз будет вызываться?

Цитата:
Код AS3:
buff == event.bytesLoaded
Вот это вот в общем случае неправильно. Данных может быть больше, чем вы считали в предыдущий раз (два сообщения подряд). Кстати, а bytesLoaded изменяется в следующем событии при чтении данных или нет? Просто либо при чтении следуюшей порции вы не получите правильных bytesLoaded (потому что считали уже один байт), либо не прочитаете вторую "порцию" информации (потому что bytesLoaded для нее будет суммой длин двух записей, а не длиной второй записи).

Старый 13.02.2012, 01:59
t4arty вне форума Посмотреть профиль Отправить личное сообщение для t4arty Найти все сообщения от t4arty
  № 3  
Ответить с цитированием
t4arty

Регистрация: May 2010
Адрес: пространство в положении
Сообщений: 219
где-то и когда-то говорили, что флеш(не air) с UDP не работает, а тока с TCP.
А так как Вы используете в сервере подключение через UDP, возможно в этом трабл.(т.к UDP у Вас будет слать пакеты, а дошли они или нет их уже не волнует)

Старый 13.02.2012, 21:08
VInchensoo вне форума Посмотреть профиль Отправить личное сообщение для VInchensoo Найти все сообщения от VInchensoo
  № 4  
Ответить с цитированием
VInchensoo

Регистрация: Jan 2012
Сообщений: 9
Цитата:
Никто не гарантирует, что даныне придут "пакетом". И в протоколе обмена нет понятия "порция" данных. Причин, по которым могут изменяться "размеры" получаемого блока данных - масса. Поэтому нужно правильно разбирать поток.
Я и пытаюсь его поэтому разбирать, а не тупо читать при проверке. Но, во-первых, как-то странно, что пакет рвется, когда он имеет размер ~20 байт. Я понял бы, если 20кб, но не байт же.

Причем на сервер все приходит сразу, там такой ошибки нет, эксепшина не было ни разу за время тестов.

В любом случае, я поставил проверку, которая в теории должна работать, чтобы не читать данные, пока они не пришли до конца.
Функции проверки буфера на непустоту я тоже не особо нашел.
Цитата:
Ну так может все данные уже пришли при втором вызове функции, откуда он третий раз будет вызываться?
Нет, судя по трассировщику данные не пришли(выводил значение bytesLoaded). Либо я не правильно понимаю, что храниться в bytesLoaded.
Цитата:
Вот это вот в общем случае неправильно. Данных может быть больше, чем вы считали в предыдущий раз (два сообщения подряд).
Вот этот момент я не особо понял. То, что я считываю- записано руками на сервере. Откуда данных будет больше? Это же не цпп с его низкоуровневой сетью, в bytesLoaded не должен учитываться заголовок пакета и прочий "системный трафик", который специфичен именно для реализации TCP.

Что вы подразумеваете под тем, что данных может быть больше?
Цитата:
Кстати, а bytesLoaded изменяется в следующем событии при чтении данных или нет? Просто либо при чтении следуюшей порции вы не получите правильных bytesLoaded (потому что считали уже один байт), либо не прочитаете вторую "порцию" информации (потому что bytesLoaded для нее будет суммой длин двух записей, а не длиной второй записи).
Ну вообще оно меняется, только я не смог понять, как именно. Обычно в консоль IDE выдается 2 строчки, первый раз грузит 4-5, потом может 9, может 11, когда как(байт).

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

Вообще, официальная документация adobe как-то крайней скупо поясняет, что такое bytesLoaded, и как его готовить.

Я считал, что это число загруженных байт, которое не меняется, т.е. во время первой проверки считано 7 байт, во время второй 11, значит вторая часть- 4 байта. Еще 1 не дошел(ну это в данном случае).

Другого способа(человечного) проверить, что данные пришли, и пришло именно столько, сколько нужно- я не нашел. Проверки на пустоту буфера я тоже не нашел.

Может вы можете подсказать, как это вся правильно организовать именно на флеше, может с какими-то пруфами\примерами кодов. Буду премного благодарен.
Цитата:
где-то и когда-то говорили, что флеш(не air) с UDP не работает, а тока с TCP.
А так как Вы используете в сервере подключение через UDP, возможно в этом трабл.(т.к UDP у Вас будет слать пакеты, а дошли они или нет их уже не волнует)
На сервере используется ТСР, как и в клиенте.


Последний раз редактировалось VInchensoo; 13.02.2012 в 21:27. Причина: Тег поправил
Старый 13.02.2012, 22:30
maxkar вне форума Посмотреть профиль Отправить личное сообщение для maxkar Найти все сообщения от maxkar
  № 5  
Ответить с цитированием
maxkar

Регистрация: Nov 2010
Сообщений: 497
Цитата:
Сообщение от VInchensoo Посмотреть сообщение
Но, во-первых, как-то странно, что пакет рвется, когда он имеет размер ~20 байт. Я понял бы, если 20кб, но не байт же.
У вас localhost и данные отправляются процессу сразу после того, как их положили в системный сокет. В Java-коде данные в системный сокет кладутся несколько раз. Вообще это плохо, но пока у вас код не заработает - наоборот, хорошо . 20Кб - это странное желание. В ethernet 100 обычно размер пакета не превышает 1,5 кб. На магистралях могут использоваться и технологии, где максимальный размер пакета - несколько десятков байт, так что все может быть.

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


Цитата:
Вот этот момент я не особо понял. То, что я считываю- записано руками на сервере. Откуда данных будет больше? Это же не цпп с его низкоуровневой сетью, в bytesLoaded не должен учитываться заголовок пакета и прочий "системный трафик", который специфичен именно для реализации TCP.

Что вы подразумеваете под тем, что данных может быть больше?
Возможно, это я вас неправильно понял. Я думал, вы несколько раз одно и то же отправляете. И не все сообщения приходят. Если вы одно сообщение вычитать не можете - это другое. Да, кстати, в c++ вы тоже низкоуровеневые данные не увидите при использовании сокетов.

Цитата:
Другого способа(человечного) проверить, что данные пришли, и пришло именно столько, сколько нужно- я не нашел. Проверки на пустоту буфера я тоже не нашел.
Первое вы точно кроме как "вручную" не проверите. Проверка на пустоту - socket.available. Но это скажет только, что "пока еще данных нет."

Цитата:
Может вы можете подсказать, как это вся правильно организовать именно на флеше, может с какими-то пруфами\примерами кодов. Буду премного благодарен.
Попробуйте на каждой операции вычитывать весь socket.available байт в свой буфер. Если у вас не получается вычитать все данные, возможно, flash не отправляет socketData, если из сокета не было чтения (хотя бы одного байта). Свой буфер затем можно копировать целиком (или по частям) в ByteArray, и уже из него читать данные. Алгоритм примерно такой:
  1. Считать данные в свой буфер (byte[]).
  2. Проверить, достаточно ли данных в буфере для сообщения.
  3. Если данных не достаточно, выходим и ждем следующего события
  4. Если данных достаточно - копируем данные в ByteArray, оттуда разбираем данные. "Очищаем" буфер, отмечаем в буфере "свободное" место
Для начала буфер можно делать "сплошной" и после выборки из него сообщения сдвигать байты. Потом можно будет сделать кольцевой буфер, он чуть побыстрее будет (но и сложнее в реализации).

Старый 14.02.2012, 21:26
VInchensoo вне форума Посмотреть профиль Отправить личное сообщение для VInchensoo Найти все сообщения от VInchensoo
  № 6  
Ответить с цитированием
VInchensoo

Регистрация: Jan 2012
Сообщений: 9
Цитата:
У вас localhost и данные отправляются процессу сразу после того, как их положили в системный сокет. В Java-коде данные в системный сокет кладутся несколько раз. Вообще это плохо, но пока у вас код не заработает - наоборот, хорошо . 20Кб - это странное желание. В ethernet 100 обычно размер пакета не превышает 1,5 кб. На магистралях могут использоваться и технологии, где максимальный размер пакета - несколько десятков байт, так что все может быть.
Ну у меня нету огромного опыта работы на java(его вообще крайне мало, есть опыт на других Яп, поэтому пишу и параллельно изучаю java), поэтому не знал, что несколько раз.
Про 20 кб- это скорее из области фантастики, аватарку передать или еще что, но, думаю, такие вещи лучше делать специальными компанентами типа URLLoader.

Цитата:
Если вы привели весь код, то вы не можете определить, "все" или "не все" приходит сразу. Все вызовы в java у вас - блокирующие. Если им не хватает данных, они ждут, пока не придет следующий фрагмент.
Опять-таки, сказалось мое незнание матчасти, буду знать. К сожалению, я только догадываюсь, как многие методы работают.

Цитата:
Возможно, это я вас неправильно понял. Я думал, вы несколько раз одно и то же отправляете. И не все сообщения приходят. Если вы одно сообщение вычитать не можете - это другое. Да, кстати, в c++ вы тоже низкоуровеневые данные не увидите при использовании сокетов.
Да, я отправляю данные 1 раз и не могу их нормально вычитать. Событие слушателя прихода данных вызывается ровно 2 раза и не более, у меня уже батхерт от этой проблемы.

Цитата:
Первое вы точно кроме как "вручную" не проверите. Проверка на пустоту - socket.available. Но это скажет только, что "пока еще данных нет."
Да, я придумал как это использовать, без вычитывания данных, читайте ниже.
Цитата:
Попробуйте на каждой операции вычитывать весь socket.available байт в свой буфер. Если у вас не получается вычитать все данные, возможно, flash не отправляет socketData, если из сокета не было чтения (хотя бы одного байта). Свой буфер затем можно копировать целиком (или по частям) в ByteArray, и уже из него читать данные. Алгоритм примерно такой:
1. Считать данные в свой буфер (byte[]).
2. Проверить, достаточно ли данных в буфере для сообщения.
3. Если данных не достаточно, выходим и ждем следующего события
4. Если данных достаточно - копируем данные в ByteArray, оттуда разбираем данные. "Очищаем" буфер, отмечаем в буфере "свободное" место
Сейчас я делал по такому алгоритму:
1. При срабатывании события я считывал первый байт- число данных в буфере, которое должно быть
2. Далее делал проверку на socket.available, равно или нет числу байт, которой должно там лежать. Если равно, то вызываем обработчик пришедшего пакета.

Снова все уперлось в эту магическую цифру: 2 вызова сокетдата.

Я решил проверить ваше предположение, что событие не вызывается, если не было чтение из буфера после прихода данных.

Каждый раз, когда что-то пришло, я вызывал проверку на то, что socket.available != 0 и считывал 1 байт(чтобы факт считывания был).

Те же бараны в итоге- 2 считывания и все, больше события сокетдата не вызывается.

Цитата:
Для начала буфер можно делать "сплошной" и после выборки из него сообщения сдвигать байты. Потом можно
будет сделать кольцевой буфер, он чуть побыстрее будет (но и сложнее в реализации).
Обернуть, в принципе, не проблема, но, т.к. проблема не в том, что отсутствует считывание, а в чем-то ином, сейчас это ничего не даст.

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

Тут 2 подводных камня:
1. Вдруг событие сработает на 2 раза, а 3, не понятно, что будет с таймером.
2. Этот способ идиотский =)

Может есть еще варианты/предположения, из-за чего это может быть? Я уже грешил в сторону ОС, но, думаю, вряд ли это связано, вряд ли проблема в железе или ОС.

Может еще что-то можете предположить?

Старый 14.02.2012, 21:53
maxkar вне форума Посмотреть профиль Отправить личное сообщение для maxkar Найти все сообщения от maxkar
  № 7  
Ответить с цитированием
maxkar

Регистрация: Nov 2010
Сообщений: 497
Код:
Может еще что-то можете предположить?
Угу. Размер пакета вычисляется неправильно на стороне сервера. Правильный размер 1 + 1 + (2 + 4 * 1) = 8 байт. Формат записи writeUTF описан в javadoc.
А вообще порекомендую еще не руками вычислять размер "сообщения", а формировать сообщение и брать его размер.
Код:
final ByteArrayOutputSteram baos = new ByteArrayOutputStream();
final DataOutput dos = new DataOutputStream(baos);
writeMessage(daos);
dos.close();
final byte[] message = baos.toByteArray();
sos.writeByte(message.length);
sos.write(message);
Вот как-то так. Писал по памяти, но идея там верная.

Цитата:
Каждый раз, когда что-то пришло, я вызывал проверку на то, что socket.available != 0 и считывал 1 байт(чтобы факт считывания был).
Учтите, что socket.read изменяет socket.available! Ну и вообще моя идея была не верна, все приходит нормально, у вас в длине наврано.

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

Старый 15.02.2012, 20:47
VInchensoo вне форума Посмотреть профиль Отправить личное сообщение для VInchensoo Найти все сообщения от VInchensoo
  № 8  
Ответить с цитированием
VInchensoo

Регистрация: Jan 2012
Сообщений: 9
Цитата:
Угу. Размер пакета вычисляется неправильно на стороне сервера. Правильный размер 1 + 1 + (2 + 4 * 1) = 8 байт. Формат записи writeUTF описан в javadoc.
Вы немного не правы. В данном случае да, размер оказался 8 байт. Но это не всегда так. Например, если записать на форуме вместо "test" слово "тест", то каждый символ будет занимать уже по 2 байта, ибо это кириллица в юникоде. Вики пояснила это тем, что лишний байт для представления латиницы просто не используется, дабы не хавать лишнюю память. Снова не понятно, что делать, ибо реализация аналога sizeof монстроподобна.
Цитата:
А вообще порекомендую еще не руками вычислять размер "сообщения", а формировать сообщение и брать его размер.
Планировалось, что этим будет заниматься класс, в который я оберну работу с сокетом и буфером, поскольку быстро найти инфу по этому поводу не удалось, а желания копать не было=)
Цитата:
Учтите, что socket.read изменяет socket.available!
Да, это учитывалось, байт размера не учитывался.
Цитата:
И на будущее рекоменду в случае проблем трейсить также и все приходящие (считываемые) данные. Вы бы заметили, что пакет меньше, чем написано.
Учту на будущее.

Из-за двойного стандарта по строкам и символам вижу 2 пути:
1. Проверять, в каком диапазоне символ и, по результатам проверки, считать 1 или 2 байта.
2. Сделать или найти аналог функции sizeof из цпп и использовать его для того, чтобы записать размер строки в байтах. Тут придется привести к двухбайтовому числу этот самый размер(хотя это не проблема, на java точно).

Может что-нибудь посоветуете? Ибо получается слишком "обернутая обертка", простите за тавтологию, т.е. надстройка над надстройков.

Старый 15.02.2012, 21:27
maxkar вне форума Посмотреть профиль Отправить личное сообщение для maxkar Найти все сообщения от maxkar
  № 9  
Ответить с цитированием
maxkar

Регистрация: Nov 2010
Сообщений: 497
Цитата:
ибо реализация аналога sizeof монстроподобна.
Вам не нужна реализация sizeof! Она не то считает. Если учеть особенности реализации java.lang.String, то она считает совсем-совсем не то!
Ссылку на документацию вы не смотрели. Хотя там была неправильная ссылка . Вот правильная ссылка. Там цикл и два if'а внутри. Ничего сложного.

Цитата:
Планировалось, что этим будет заниматься класс, в который я оберну работу с сокетом и буфером, поскольку быстро найти инфу по этому поводу не удалось, а желания копать не было=)
Пусть он и пишет в byteOutputStream а по завершении сообщения получает длину!

Цитата:
Ибо получается слишком "обернутая обертка"
Ну и пусть получается. Получающиеся слои работают на разном уровне абстракции. Один - транспортный уровень (сокет, буфер). Второй - формирование "сообщения" (пакета с длиной). Третий - формированием "осмысленного" сообщения (уже в терминах логики приложения). Все нормально.

Старый 15.02.2012, 21:52
VInchensoo вне форума Посмотреть профиль Отправить личное сообщение для VInchensoo Найти все сообщения от VInchensoo
  № 10  
Ответить с цитированием
VInchensoo

Регистрация: Jan 2012
Сообщений: 9
Цитата:
Вам не нужна реализация sizeof! Она не то считает. Если учеть особенности реализации java.lang.String, то она считает совсем-совсем не то!
Ссылку на документацию вы не смотрели. Хотя там была неправильная ссылка . Вот правильная ссылка. Там цикл и два if'а внутри. Ничего сложного.
Ну да, sizeof в цпп вернуло бы размер объекта класса String, но вы поняли, что я имел ввиду под аналогом этой функции. Похоже, в лоб тут действовать глупо. Приведенный линк я изучал после предыдущего вашего поста, в гугле нашел.
Цитата:
Пусть он и пишет в byteOutputStream а по завершении сообщения получает длину!
Буду курить завтра, что это за штука. Мне кажется, я вас уже утомил) Спасибо.
Для флеша аналогичные методы присутствуют? Думаю, есть смысл сделать проверку, вдруг и до явы что-то не будет доходить сразу целиком. С другой стороны, методы java вроде бы ожидающие, так что не знаю, есть ли смысл?
Цитата:
Ну и пусть получается. Получающиеся слои работают на разном уровне абстракции. Один - транспортный уровень (сокет, буфер). Второй - формирование "сообщения" (пакета с длиной). Третий - формированием "осмысленного" сообщения (уже в терминах логики приложения). Все нормально.
В принципе вы правы, но с точки зрения кода работать это будет медленнее, хотя бы за счет времени, которое уйдет на создание экземпляра обертки в памяти. Но, я думаю, это не критично, если экономить на таких вещах- можно просто сойти с ума с оптимизацией.


Последний раз редактировалось VInchensoo; 15.02.2012 в 21:55.
Создать новую тему Ответ Часовой пояс GMT +4, время: 19:58.
Быстрый переход
  « Предыдущая тема | Следующая тема »  

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


 


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


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