Перейти к содержанию
Fire Monkey от А до Я

Wovan2

Пользователи
  • Постов

    45
  • Зарегистрирован

  • Посещение

Сообщения, опубликованные Wovan2

  1. 48 минут назад, kami сказал:

    Одна из ошибок здесь.

    Во-первых, ReadString уже возвращает строку. В той кодировке, которую правильно воспримет memo.

    Без Utf8ToAnsi сервер (это серверный код) пишет "Привет".

    Свойство s.Encoding только для чтения.

    Пока помогает только прямое перекодирование, как в предыдущем моем ответе.

  2. Пока докопался до следующего.

    В Delphi строка закодирована  в ANSI. (Или в Windows???)

    Замена на сервере s.WriteString('И тебе привет');//AnsiToUtf8   And You, Hi

    на 
     a := TEncoding.ANSI.GetBytes('И тебе привет');
     s.Write(TEncoding.Convert(TEncoding.ANSI, TEncoding.UTF8, a), Length(a) * 2);

    где  a : TBytes;

    решает проблему. Только как-то вроде не красиво. Может как можно поизящнее переписать? Другими функциями, которых дофига и больше?

  3. Здравствуйте.

    Немного предыстории. Нужно соорудить клиент-сервер. Клиент на Android, сервер на Windows. 

    Собственно перебираю варианты. 

    1. Сделал с помощью DataSnap. Работает. Но очень громоздко и не понятно.

    2. Tethering. Так и не смог реализовать обмен данными между сервером и клиентом типа запрос-ответ.

    3. IdTCPClient, IdTCPSever. Раньше (D7) опыт работы отрицательный, но в FMX, видимо, альтернативы нет.

    Так что сейчас предварительно реализую простой диалог запрос-ответ. Причем все вроде работает. И кода немного и быстро. НО. Уперся в кодировку. На клиенте (Android) не получается прочитать кириллицу. Сначала пробовал общаться Read, Write и передавать просто строки. Пробовал всякие индийские компоненты для кодировки. Но без понимания ничего и не получилось. В интернете где-то наткнулся на предложение передавать потоками. Реализовал. Стало лучше, но проблема с кодировкой не пропала. Скажем так, эта проблема уменьшилась вдвое. На стороне сервера все ОК. На стороне клиента либо знаки "????", либо ромбики с вопросами, либо ошибка "No mapping for the unicode character exists in the target multi-byte code page". Это я бессистемно перебирал варианты кодировок. К сожалению до сих пор так и не понимаю эти перекодировки. Вернее не так. Не понимаю откуда и куда идут какие кодировки. 

    В общем помогите пожалуйста советом или кодом. Ниже код с которым экспериментирую. (код не большой)

    Сервер

    procedure TForm1.IdTCPServerExecute(AContext: TIdContext);
    var
     s : TStringStream;
    begin
     s := TStringStream.Create;
     mem.Lines.Add(AContext.Connection.Socket.Binding.PeerIP);
     AContext.Connection.IOHandler.ReadStream(s);
     s.Position := 0;
     mem.Lines.Add(Utf8ToAnsi(s.ReadString(s.Size)));
     s.Free;
     s := TStringStream.Create;
     s.WriteString('И тебе привет');//  And You, Hi с этим все нормально, проблема только с русскими буквами
     s.Position := 0;
     AContext.Connection.IOHandler.Write(s,s.Size,true); //здесь s.Size = 13. в 26 никак не получается превратить, может быть тут что-то?
     s.Free;
    end;

    Клиент

    procedure TfrmMainClient.btn1Click(Sender: TObject);
    var s:TStringStream;
    begin
     s := TStringStream.Create;
     try
     IdTCPClient.Connect;
     if IdTCPClient.Connected
      then mem.Lines.Add('Есть контакт с сервером ' + IdTCPClient.Socket.Binding.PeerIP);
     s.WriteString('Привет');
     s.Position := 0;
      IdTCPClient.IOHandler.Write(s, s.Size, true);//здесь заметил, что s.Size = 12, похоже на unicode
      s.Clear;
      IdTCPClient.IOHandler.ReadStream(s);
      s.Position := 0;
      Mem.Lines.Add(s.ReadString(s.Size)); //ошибка здесь
     finally
      IdTCPClient.Disconnect;
      if Assigned(s)
       then s.Free;
     end;
    end;

    Этот код приводит на клиента к ошибке  "No mapping for the unicode character exists in the target multi-byte code page". На сервере сообщение "Привет" видно нормально.

    Помогите, кто чем может.

    ЗЫ. Тему "Как Получить текст по TCP (Indy) в нужной кодировке?" смотрел. Но либо она не актуальна (инди уже изменился), либо я не смог правильно понять. По крайней мере пробовал использовать это в самом начале, когда пытался передавать строки напрямую. В указанном коде IIdTextEncoding не представляю куда и как вставить.

    зы2: Извините. Пишу на Delphi 10.2. Indy 10.6.2(в комплекте с Delphi). Windows 10. Android 4 и 5

  4. И снова здравствуйте. Хотя и разговариваю сам с собой, но все же, может кто еще и откликнется.

    В общем продолжаю мучить Tethering. В событии TAPServerAfterReceiveData удалось перехватить отправленную моим клиентом команду.

    Но дальше пока не пошло. Навскидку имеется две проблемы.

    Первая. Клиент передает команду на сервер так:

    LS := TStringList.Create;
    LS.Add('Привет - это я!');          
    ACommand := TTetheringCommand.Create('MyCommand', 33, ['par1','par2'], LS);
    Resp := TAPClient.SendCommandWithResponse(TMClient.RemoteProfiles[0], ACommand);

    На сервере в событии TAPServerAfterReceiveData эта команда перехватывается. Но я почему-то не могу вынуть переданный объект, а именно TStringList. Делаю так:
    tc := TTetheringCommand.Create(TBytes(ADataBuffer));
     try
     memConnectedInfo.Lines.Add(tc.ToString);// TStringList(tc.Data)[0]
     if tc.Command = 'MyCommand'
      then
       begin
        memConnectedInfo.Lines.Add('Сообщ. ' + TStringList(tc.Data)[0]); 
        Resp := TTetheringCommand.Create('RetMyCommand', 3, [], tc.Data);
        Result := TByteDynArray(Resp.ToBytes); //это ни на что не влияет
       end
      else Result := ADataBuffer;
     except
      Result := ADataBuffer;
     end;

    Срабатывает исключение при обращении TStringList(tc.Data)[0]. Что я делаю не так? Или в ADataBuffer объект отсутствует? На клиенте команда 'RetMyCommand' тоже получается, опять не могу получить данные переданного объекта. Правда делается это в событии TAPServerBeforeSendData:

     tc := TTetheringCommand.Create(TBytes(ADataBuffer));
     try
     memConnectedInfo.Lines.Add(tc.Command);
     if tc.Command = 'RES_BADCOM'
      then
       begin
        LS := TStringList.Create;
        LS.Add('Привет - это я с сервера!');
        Resp := TTetheringCommand.Create('RetMyCommand', 2, [], TObject(LS));
        Result := TByteDynArray(Resp.ToBytes);
        memConnectedInfo.Lines.Add(TTetheringCommand.Create(TBytes(Result)).ToString);
       end
      else Result := ADataBuffer;
     except

     end;

    Вторая проблема. Похоже, что вообще подход через указанные события совсем не правильный. Ковыряние в исходниках вроде как говорит о том, что команды используются только самим тетрингом, потому что там используется специфический набор команд. Как использовать свои команды не совсем ясно, хотя и очевидно, что сами нестандартные команды передаются в обе стороны. Мне кажется, что должно быть как-то изящнее и проще. Но как? Не приложу ума. Ведь кроме получения команды на стороне сервера надо передать ответ клиенту, причем этот ответ должен попасть в функцию SendCommandWithResponse, вызванную на клиенте. Есть соображения?

    Проверка в TAPServerAfterReceiveData
     tc := TTetheringCommand.Create(TBytes(ADataBuffer));
     if tc.Data = nil возвращает ИСТИНУ. Объект действительно где-то пропал.

     

  5. Кое-что прояснилось при ближайшем рассмотрении. События TAPServerBeforeSendData иTAPServerAfterReceiveData по сути функции. И результат этих функций нужно обязательно переопределять Result := ADataBuffer. При этом, видимо, можно что-то поправить в команде, принимаемой или отсылаемой. Событие TAPServerAfterReceiveData на сервере срабатывает периодически, возможно по какому-то таймауту, с пустой командой.

    Кстати среди всех этих событий мне удалось выловить и свою отправленную с клиента команду 'MyCommand'. Осталось обработать ее персонально.:)

  6. К Indy у меня плохое отношение. Глючные, тормозные и громоздкие компоненты. На D7 пользуюсь Synapse, не в пример проще и предсказумее. Да, я знаю, Tethering реализован на Indy (возможно с этим и связано непостоянство установки соединения). Но в данном случае хотелось попробовать саму технологию, которая везде расписана как панацея от всех сетевых проблем. А оказывается не все так просто. Только что обнаружил, что метод Connect у меня не соединяет профили (выяснил это при скурпулезной отладке на Android). Хотя в примерах Delphi все работает нормально. Буду искать где накосячил. Возможно и код приведенный выше может оказаться рабочим.

    Добавлено 12:11
    Товарищи. Это пипец какой-то. Если на сервере задействую события TAPServerBeforeSendData или TAPServerAfterReceiveData, клиент перестает подключаться к профилю сервера. Без этих событий все нормально подключается.

  7. Порядок событий на сервере


    TAPServerBeforeSendData
    wrWc0I8ol7
    TAPServerAfterReceiveData
    [пустая строка]
    TAPServerBeforeSendData
    {7FCD2085-28F1-4B42-9446-A73A0BC2873F}
    TAPServerAfterReceiveData
    {38BAA5D2-B0C3-E711-8018-AF8831B0713E}
    TAPServerBeforeSendData
    Error
    TAPServerAfterReceiveData
    [пустая строка]
    TAPServerAfterReceiveData
    [пустая строка]

    После имени события выводится свойство TTetheringCommand.Command (как в коде выше)

    Визуально похоже на обмен пакетами при сетевом общении

    Сперва хеш пароля, потом обмен адресами, потом передача произвольных данных (в моем случае пустые объекты)

    Ни у кого никаких мыслей не возникает? Мне бы понять в каком месте на сервере можно заполнить структуру ответа.

    Попытка покопаться в исходных кодах ни к чему не привела. Есть интересная процедура DoOnIncomingData, но по какому событию она запускается  я не понял. Соответственно никакого события OnIncomingData у объекта TTetheringAppProfile нет.

  8. На клиенте выполняю следующий код:

        LS := TStringList.Create;
        LS.Add('Привет - это я!');
        ACommand := TTetheringCommand.Create('MyCommand', 33, ['par1','par2'], LS);

        ed1.Text := 'Начало';
        try
        Resp := TAPClient.SendCommandWithResponse(TMClient.RemoteProfiles[0], ACommand);
        except
        on e:Exception do
         ShowMessage(e.Message)
        end;
        ed1.Text := 'Конец'

    При выполнении всегда срабатывает исключение "Operation aborted".

    На сервере в обработке TAPServerAfterReceiveData

    memConnectedInfo.Lines.Add('TAPServerAfterReceiveData');
     tc := TTetheringCommand.Create(TBytes(ADataBuffer));
     try
     memConnectedInfo.Lines.Add(tc.Command);
     finally

     end;

    Из нескольких срабатываниях выводится идентификатор Клиента. Хотя в Command вставляю 'MyCommand'.

    Ни одного примера в во всем инете не нашел.

    В 03.11.2017 в 19:12, wamaco сказал:

    Эту статью тоже штудировал. Мало чем отличается по содержанию от всех остальных. Разве немного подробнее. Но TTetheringCommand нет и там.

  9. В 03.11.2017 в 17:34, kami сказал:

    TTetheringCommand - есть метод Create, принимающий TBytes

    А вот почему 3 пары событий - это не знаю...

    В событиях AfterReceiveData, BeforeSendData параметр имеет тип TByteDynArray. Как его в тип TBytes перевести? Туплю. Только из Delphi 7 вылез.

  10. 29 минут назад, kami сказал:

    Там искать нечего. В глубине тетеринга эта команда - всего лишь совокупность

    
    SendCommand(AConnection, ACommand);
    Result := ReceiveCommand(AConnection);

    Получается, что (как видно из названия и кода) она отправляет команду (как обычно, т.е. обработка на приеме ничем не отличается) и ждет, пока получит команду в ответ.

    Единственное что - следует самому создавать TTetheringCommand со своими параметрами, а не пользоваться готовыми SendStream и SendString

    Не могу сообразить как обрабатывать на сервере эти команды. При вызове функции SendCommandWithResponse на клиенте, на сервере срабатывают события BeforeSendData, AfterReceiveData, BeforeSendData, AfterReceiveData, BeforeSendData, AfterReceiveData. Именно так: 3 раза парами. Параметр ADataBuffer: TByteDynArray в каждом из этих вызовов разный. Видимо в этом TByteDynArray и прячется исходная структура TTetheringCommand? Как их преобразовывать друг в друга?

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

  11. 26 минут назад, krapotkin сказал:

    про тетеринг есть хорошее видео на ЮТ эмбаркадеро раша от Совцова

    Смотрел уже. Ничего нового. Все то же, что и во всем инете. Шаблонные задачи во всех источниках одинаковые: подключились, послали, в событии получили.

  12. Может, кто владеет этой технологией, подскажет как реализовать выполнение  функции на стороне сервера с возвратом результата клиенту без этого SendCommand, Другие клиенты не должны видеть друг друга и не должны перехватывать чужие данные.

  13. Здравствуйте.

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

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

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

    Может кто может хотя бы намекнуть в какую сторону искать.

  14. Активность на этом форуме не очень. Видимо из-за того, что все только учатся сами.

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

    bc := JStringToString(csIntent.getStringExtra(StringToJString('barcode_string')))

    Всем спасибо.

  15. Здравствуйте.

    Пишу на Delphi 7. И все было нормально пока не у нас не появился терминал сбора данных на Androide. На Delphi 10 написал простенькое приложение. Но тут встала проблема с занесением данных в поле ввода со сканера штрихкода. В инете нашел, что это дело просто решается интентами. Вообще в windows я подобное делал: вешал процесс, следящий за com-портом, и при появлении данных на нем пересылал их куда надо. В Android должно быть не сложнее. Но я запутался во всей этой куче параметров и функций, передаваемых и получаемых "намерениями".

    В общем вот что мы имеем на данный момент.

    В настройках сканера прописано:
     

    Intent output - android.intent.ACTION_DECODE_DATA
    
    Intent string extra - barcode_string

    На просторах нашел запуск BroadcastReceiver.

    На событие получения сообщения повесил 

    ed.Text := JStringToString(csIntent.getExtras.getString(TJIntent.JavaClass.EXTRA_INTENT));

    где csIntent параметр из  BroadcastReceiverOnReceive(csContext: JContext; csIntent: JIntent); Но ничего не выходит. 

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

    Причем строка ed.Text := JStringToString(csIntent.getAction); возвращает в Text название интента: "android.intent.ACTION_DECODE_DATA".

    Помогите разобраться со структурой JIntent. Спасибо.

     

     

×
×
  • Создать...