Отсутствуют данные при отправке данных из ClientSocket (Mobile) в ServerSocket (Server)

Я использую FireMonkey в Delphi 10.1 Berlin для разработки мобильного клиентского приложения Android и использую VCL в Delphi 10.1 Berlin для разработки серверного приложения Windows.

В мобильном приложении я использую TIdTCPClient для отправки следующей записи:

PSampleReq = ^TSampleReq ;
TSampleReq = packed record
  Value1: array [0..10] of Char;
  Value2: array [0..59] of Char;
  Value3: array [0..40] of Char;
  Value4: Int64;
  Value5: array [0..9] of Char;
  Value6: array [0..9] of Char;
  Value7: Integer;
end;

Я заполнил пакет данными и отправляю пакет, используя следующий код:

FIdTCPClient.IOHandler.Write(RawToBytes(TSampleReq,SizeOf(TSampleReq)));

При чтении данных в приложении "Сервер" я не могу прочитать поля Value5, Value6 и Value7. Ниже приведен код, который считывает данные:

Move(tyTIDBytes[0], SampleReq, SizeOf(TSampleReq));

Для получения данных, которые отправляются из клиентского сокета, я использовал TIDTcpServer и обработал приведенный ниже код в методе Execute:

TServerRecord = packed record
PointerMessage : TIndyBytes;
ClientSocket   : TIdTCPConnection;
end;

Var    
ReceivedIDBytes: TServerRecord;
begin
if not AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.InputBuffer.ExtractToBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes) ;
ReceivedIDBytes.ClientSocket := AContext.Connection;
MessageProcessorThread.ProcessMessageQueue.Enqueue(ReceivedIDBytes);
end;

После этого я обрабатываю данные из очереди и метод обработки, который я упомянул ниже:

var
InputRec: TServerRecord;
begin
InputRec := DBWorkerThread.DBWorkerQueue.Dequeue;
MessageHeaderPtr := @InputRec.PointerMessage.tyTIDBytes[0];
iHMMessageCode := StrToIntDef( Trim(MessageHeaderPtr^.MessageCode), UNKNOWN_MESSAGE_CODE);
case iHMMessageCode of
1001:
begin
Move(InputRec.PointerMessage.tyTIDBytes[0], SampleReq, SizeOf(TSampleReq));
end;
end;

И в этом я не могу прочитать поля Value5, Value6 и Value7.

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

Отправка правильного размера записи через сокет


person Work 2 Enjoy - Enjoy 2 Work    schedule 09.05.2017    source источник
comment
Вы проверили, что sizeof(TSampleReq) составляет ровно 276 байт в обоих приложениях? Вы не показали свой фактический код чтения, заполняющий tyTIDBytes, до того, как вы вызвали для него Move(). Код должен быть примерно таким: var tyTIDBytes: TIdBytes; ... AContext.Connection.IOHandler.ReadBytes(tyTIDBytes, SizeOf(TSampleReq), False); И к вашему сведению, у Инди есть функция BytesToRaw() в качестве дополнения к RawToBytes(): BytesToRaw(tyTIDBytes, SampleReq, SizeOf(TSampleReq));   -  person Remy Lebeau    schedule 09.05.2017
comment
И к вашему сведению, вы не используете оптимизированный метод, описанный в этой другой ссылке. Этот метод отправляет размер данных перед отправкой фактических данных. Эквивалент Indy будет выглядеть примерно так: tyTIDBytes := RawToBytes(SampleReq, SizeOf(TSampleReq); FIdTCPClient.IOHandler.Write(Int32(Length(tyTIDBytes))); FIdTCPClient.IOHandler.Write(tyTIDBytes); ... AContext.Connection.IOHandler.ReadBytes(tyTIDBytes, AContext.Connection.IOHandler.ReadInt32, False); BytesToRaw(tyTIDBytes, SampleReq, SizeOf(TSampleReq));   -  person Remy Lebeau    schedule 09.05.2017
comment
Пожалуйста, отредактируйте свой вопрос, чтобы показать фактический код, который вы используете на обоих концах соединения. Очевидно, что между вашим кодом отправки и кодом чтения существует несоответствие. И вы не ответили на мой предыдущий вопрос: Вы проверили, что sizeof(TSampleReq) составляет ровно 276 байт в обоих приложениях?.   -  person Remy Lebeau    schedule 22.05.2017
comment
Я добавил дополнительную информацию в свой вопрос, и часть, принимающая сервер, должна обрабатываться для разных записей, а также я пробовал ваш метод, но все же сталкиваюсь с той же проблемой.   -  person Work 2 Enjoy - Enjoy 2 Work    schedule 22.05.2017
comment
вы НЕ используете технику, которую я описал, даже близко. Вы используете ExtractToBytes() неправильно, и это корень вашей проблемы. Я отправил ответ сейчас.   -  person Remy Lebeau    schedule 22.05.2017


Ответы (1)


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

Если ваш клиент каждый раз отправляет запись фиксированного размера, вы должны считывать ровно столько байтов, ни больше, ни меньше:

var
  ReceivedIDBytes: TServerRecord;
begin
  AContext.Connection.IOHandler.ReadBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes, SizeOf(TSampleReq));  // <-- HERE!!!
  ReceivedIDBytes.ClientSocket := AContext.Connection;
  MessageProcessorThread.ProcessMessageQueue.Enqueue(ReceivedIDBytes);
end;

Однако, если размер записи зависит от кода сообщения, ваш клиент должен отправить количество байтов в записи перед отправкой фактических байтов записи:

var
  tyTIDBytes: TIdBytes;
begin
  tyTIDBytes := RawToBytes(TSampleReq, SizeOf(TSampleReq));
  FIdTCPClient.IOHandler.Write(Int32(Length(tyTIDBytes)));
  FIdTCPClient.IOHandler.Write(tyTIDBytes);
end;

И тогда сервер может прочитать количество байтов перед чтением байтов:

var
  ReceivedIDBytes: TServerRecord;
begin
  AContext.Connection.IOHandler.ReadBytes(ReceivedIDBytes.PointerMessage.tyTIDBytes, AContext.Connection.IOHandler.ReadInt32);  // <-- HERE!!!
  ReceivedIDBytes.ClientSocket := AContext.Connection;
  MessageProcessorThread.ProcessMessageQueue.Enqueue(ReceivedIDBytes);
end;
person Remy Lebeau    schedule 22.05.2017
comment
Спасибо. Он отлично работает, когда клиентское приложение работает на рабочем столе и нет пропущенных пакетов. Но когда я попытался запустить на мобильном устройстве Android, value6 не получил. И есть ли какие-либо ограничения для мобильного приложения Android firemonkey? - person Work 2 Enjoy - Enjoy 2 Work; 23.05.2017
comment
@Work2Enjoy-Enjoy2Work Показанный мной код работает одинаково на всех платформах. Если это не работает для вас, где-то в вашем коде все еще должно быть несоответствие. Вам нужно отлаживать свой код. Вы ВСЕ ЕЩЕ не ответили на мой предыдущий вопрос. Это последний раз, когда я собираюсь спросить: Является ли sizeof(TSampleReq) точно 276 байт во всех ваших приложениях? Проверяли ли вы, что TIdIOHandler.ReadInt32() каждый раз возвращает ожидаемое значение? Если нет, убедитесь, что вы используете актуальную версию Indy. IIRC, на Android была ошибка с порядком байтов, которую я исправил некоторое время назад. - person Remy Lebeau; 23.05.2017
comment
Спасибо @Remy. Теперь я получаю правильные данные на стороне сервера. И есть ли ограничения по размеру для конкретных пакетов, которые необходимо отправить? - person Work 2 Enjoy - Enjoy 2 Work; 26.05.2017
comment
@Work2Enjoy-Enjoy2Work ограничен 2 ГБ, максимальное значение Int32. - person Remy Lebeau; 26.05.2017