![](https://fire-monkey.ru/uploads/set_resources_12/84c1e40ea0e759e3f1505eb1788ddf3c_pattern.png)
Wovan2
-
Постов
45 -
Зарегистрирован
-
Посещение
Сообщения, опубликованные Wovan2
-
-
Пока докопался до следующего.
В 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;
решает проблему. Только как-то вроде не красиво. Может как можно поизящнее переписать? Другими функциями, которых дофига и больше?
-
Здравствуйте.
Немного предыстории. Нужно соорудить клиент-сервер. Клиент на 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
-
И снова здравствуйте. Хотя и разговариваю сам с собой, но все же, может кто еще и откликнется.
В общем продолжаю мучить 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;
exceptend;
Вторая проблема. Похоже, что вообще подход через указанные события совсем не правильный. Ковыряние в исходниках вроде как говорит о том, что команды используются только самим тетрингом, потому что там используется специфический набор команд. Как использовать свои команды не совсем ясно, хотя и очевидно, что сами нестандартные команды передаются в обе стороны. Мне кажется, что должно быть как-то изящнее и проще. Но как? Не приложу ума. Ведь кроме получения команды на стороне сервера надо передать ответ клиенту, причем этот ответ должен попасть в функцию SendCommandWithResponse, вызванную на клиенте. Есть соображения?
Проверка в TAPServerAfterReceiveData
tc := TTetheringCommand.Create(TBytes(ADataBuffer));
if tc.Data = nil возвращает ИСТИНУ. Объект действительно где-то пропал. -
Кое-что прояснилось при ближайшем рассмотрении. События TAPServerBeforeSendData иTAPServerAfterReceiveData по сути функции. И результат этих функций нужно обязательно переопределять Result := ADataBuffer. При этом, видимо, можно что-то поправить в команде, принимаемой или отсылаемой. Событие TAPServerAfterReceiveData на сервере срабатывает периодически, возможно по какому-то таймауту, с пустой командой.
Кстати среди всех этих событий мне удалось выловить и свою отправленную с клиента команду 'MyCommand'. Осталось обработать ее персонально.
-
К Indy у меня плохое отношение. Глючные, тормозные и громоздкие компоненты. На D7 пользуюсь Synapse, не в пример проще и предсказумее. Да, я знаю, Tethering реализован на Indy (возможно с этим и связано непостоянство установки соединения). Но в данном случае хотелось попробовать саму технологию, которая везде расписана как панацея от всех сетевых проблем. А оказывается не все так просто. Только что обнаружил, что метод Connect у меня не соединяет профили (выяснил это при скурпулезной отладке на Android). Хотя в примерах Delphi все работает нормально. Буду искать где накосячил. Возможно и код приведенный выше может оказаться рабочим.
Добавлено 12:11
Товарищи. Это пипец какой-то. Если на сервере задействую события TAPServerBeforeSendData или TAPServerAfterReceiveData, клиент перестает подключаться к профилю сервера. Без этих событий все нормально подключается. -
Порядок событий на сервере
TAPServerBeforeSendData
wrWc0I8ol7
TAPServerAfterReceiveData
[пустая строка]
TAPServerBeforeSendData
{7FCD2085-28F1-4B42-9446-A73A0BC2873F}
TAPServerAfterReceiveData
{38BAA5D2-B0C3-E711-8018-AF8831B0713E}
TAPServerBeforeSendData
Error
TAPServerAfterReceiveData
[пустая строка]
TAPServerAfterReceiveData
[пустая строка]После имени события выводится свойство TTetheringCommand.Command (как в коде выше)
Визуально похоже на обмен пакетами при сетевом общении
Сперва хеш пароля, потом обмен адресами, потом передача произвольных данных (в моем случае пустые объекты)
Ни у кого никаких мыслей не возникает? Мне бы понять в каком месте на сервере можно заполнить структуру ответа.
Попытка покопаться в исходных кодах ни к чему не привела. Есть интересная процедура DoOnIncomingData, но по какому событию она запускается я не понял. Соответственно никакого события OnIncomingData у объекта TTetheringAppProfile нет.
-
На клиенте выполняю следующий код:
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);
finallyend;
Из нескольких срабатываниях выводится идентификатор Клиента. Хотя в Command вставляю 'MyCommand'.
Ни одного примера в во всем инете не нашел.
В 03.11.2017 в 19:12, wamaco сказал:Эту статью тоже штудировал. Мало чем отличается по содержанию от всех остальных. Разве немного подробнее. Но TTetheringCommand нет и там.
-
В 03.11.2017 в 17:34, kami сказал:
TTetheringCommand - есть метод Create, принимающий TBytes
А вот почему 3 пары событий - это не знаю...
В событиях AfterReceiveData, BeforeSendData параметр имеет тип TByteDynArray. Как его в тип TBytes перевести? Туплю. Только из Delphi 7 вылез.
-
29 минут назад, kami сказал:
Там искать нечего. В глубине тетеринга эта команда - всего лишь совокупность
SendCommand(AConnection, ACommand); Result := ReceiveCommand(AConnection);
Получается, что (как видно из названия и кода) она отправляет команду (как обычно, т.е. обработка на приеме ничем не отличается) и ждет, пока получит команду в ответ.
Единственное что - следует самому создавать TTetheringCommand со своими параметрами, а не пользоваться готовыми SendStream и SendString
Не могу сообразить как обрабатывать на сервере эти команды. При вызове функции SendCommandWithResponse на клиенте, на сервере срабатывают события BeforeSendData, AfterReceiveData, BeforeSendData, AfterReceiveData, BeforeSendData, AfterReceiveData. Именно так: 3 раза парами. Параметр ADataBuffer: TByteDynArray в каждом из этих вызовов разный. Видимо в этом TByteDynArray и прячется исходная структура TTetheringCommand? Как их преобразовывать друг в друга?
Где-то надо разобрать то, что послал клиент (как-то получить посланную структуру TTetheringCommand), потом, после нужной обработки создать такую же структуру ответа и куда-то уже ее засунуть. Не могу понять где, что и куда совать.
-
26 минут назад, krapotkin сказал:
про тетеринг есть хорошее видео на ЮТ эмбаркадеро раша от Совцова
Смотрел уже. Ничего нового. Все то же, что и во всем инете. Шаблонные задачи во всех источниках одинаковые: подключились, послали, в событии получили.
-
Может, кто владеет этой технологией, подскажет как реализовать выполнение функции на стороне сервера с возвратом результата клиенту без этого SendCommand, Другие клиенты не должны видеть друг друга и не должны перехватывать чужие данные.
-
Не могу найти ни одного примера использования. Даже в хелпе Delphi
-
Здравствуйте.
Пытаюсь через Tethering получить данные с удаленного приложения. Но все реализовано через события, и при наличии нескольких клиентов у меня возникла путаница, какому клиенту, что вернуть.
Покопался в компоненте профиля и нашел функцию SendCommandWithResponse. По описанию, вроде, то, что мне нужно. Посылаю что-то конкретному профилю и получаю конкретно мне предназначенный ответ. Беда в том, я не нашел как с этим работать. Вообще про команды ничего не нашел, даже не английском.
Похожих событий на принимающей стороне не нашел. Ведь вызвать функцию на клиенте - пол дела. Надо чтобы сервер обработал запрос и вернул результат. Или что-то не понимаю.
Может кто может хотя бы намекнуть в какую сторону искать.
-
Активность на этом форуме не очень. Видимо из-за того, что все только учатся сами.
В общем, поковырялся и вытащил считанный код с помощью следующего оператора
bc := JStringToString(csIntent.getStringExtra(StringToJString('barcode_string')))
Всем спасибо.
-
Извиняюсь не силен в С#. Сам только из Delphi 7 перешел.
-
Может отсюда http://developer.alexanderklimov.ru/android/theory/airplanemode.php можно что-то выжать? Написано на С#, но, в принципе, код понятен.
-
Здравствуйте.
Пишу на 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. Спасибо.
IdTCPClient, IdTCPSever - снова о кодировке
в TIdTCPClient
Опубликовано · Изменено пользователем Wovan2
Без Utf8ToAnsi сервер (это серверный код) пишет "Привет".
Свойство s.Encoding только для чтения.
Пока помогает только прямое перекодирование, как в предыдущем моем ответе.