Связь Bluetooth между Android / Windows, отсутствующие сообщения - PullRequest
0 голосов
/ 11 июля 2019

Я пытаюсь написать небольшой Bluetooth-протокол для отправки сообщений в кодировке B64 с устройства Android на устройство Windows и наоборот. Для этого я использую 3 темы в одном классе.

  TBluetooth = class
  private
    FBTReceiveThread: TBTReceiveThread;
    FBTSendThread: TBTSendThread;
    FBTMsgHandlerThread: TBTMsgHandlerThread;
    FBluetoothManager: TBluetoothManager;
    FServiceName: String;
    FServiceGUID: String;
    function ManagerConnected(): Boolean;
  public
    constructor create(const AServicename: STring; const aGuid: String; const AValue: TBluetoothOnMsgReady);
    destructor Destroy(); override;
    function ListPairedDevices: TBluetoothDeviceList;
    function getDeviceFromAdress(const Aadress: String): TBluetoothDevice;
    function sendMessage(const aData: STring; const aIdentifier: STring; const Aadress: String;
      const HighPriority: Boolean): TGuid;
    procedure startListening;
    // procedure stopListening;
    procedure SetBluetoothOnError(const AValue: TBluetoothOnError);
    procedure SetBluetoothOnMSGReady(const AValue: TBluetoothOnMsgReady);
    procedure SetBluetoothSendStatus(const AValue: TBLuetoothSendStatus);
    property BluetoothOnMSGReady: TBluetoothOnMsgReady write SetBluetoothOnMSGReady;
    property BluetoothOnError: TBluetoothOnError write SetBluetoothOnError;
    property BluetoothSendStatus: TBLuetoothSendStatus write SetBluetoothSendStatus;
  end;

Один для отправки сообщений, один для их получения и один для объединения частей сообщений.

SendThread

Сообщение разбивается вручную для большего контроля над отправкой и для простого объединения их в конце восстановления. Я добавляю части сообщения в очередь, и «sendthread» отправляет их одну за другой.

procedure TBTSendThread.Execute;
var
  msgToSend: TBtMsgRec;
  msgHeader, statusstr: String;
  fData: TBytes;
begin
  inherited;
  while not terminated do
    try
      if (FQueue.Count > 0) then
      begin
        TMonitor.Enter(FLock);
        try
          msgToSend := FQueue.First;
          FQueue.Delete(0);
          FSocket := msgToSend.targetDevice.CreateClientSocket(StringToGuid(FServiceGUID), True);
          FSocket.Connect;

          msgHeader := msgToSend.Identifier + '|' + GUIDToString(msgToSend.FGuid) + '|' + inttoStr(msgToSend.Length) +
            '|' + inttoStr(msgToSend.part) + '/' + inttoStr(msgToSend.max) + '|';
          fData := TEncoding.UTF8.GetBytes(msgHeader + msgToSend.Data);
          FSocket.SendData(fData);
          if (msgToSend.part = msgToSend.max) then
            dec(fQueueCount);
          if assigned(FGetSendStatus) then
            Synchronize(
              procedure
              begin
                FGetSendStatus(msgToSend.FGuid, msgToSend.Length, msgToSend.part, msgToSend.max, fQueueCount);
              end);
          Sleep(50);
        finally
          TMonitor.Exit(FLock);
          FSocket.close;
        end;
      end;
    except
      on e: Exception do
      begin
        Synchronize(
          procedure
          begin
            if assigned(FBLuetoothOnError) then
              FBLuetoothOnError(e);
          end);
      end;
    end;
end;

Получать тему

Поток получения читает буфер Socket и передает необработанную строку в поток msghandler.

procedure TBTReceiveThread.Execute;
var
  FSocket: TBluetoothSocket;
  ok: Boolean;
  LData: TBytes;
begin
  inherited;
  while not terminated do
    try
      FSocket := FServerSocket.Accept(500);
      if FSocket <> nil then
      begin
        ok := True;
        while ok do
          try
            LData := FSocket.ReceiveData;
            if (Length(LData) > 0) and assigned(FMsgHandlerThread) then
              FMsgHandlerThread.addRawMsg(TEncoding.UTF8.GetString(LData));
          except
            ok := false;
          end;
        SetLength(LData, 0);
        //sleep(50);
      end;
    except
      on e: Exception do
      begin
        Synchronize(
          procedure
          begin
            if assigned(FBLuetoothOnError) then
              FBLuetoothOnError(e);
          end);
      end;
    end;
end;

MSGHandler Thread

Поток MSGHandler берет необработанные строки и объединяет их в полные сообщения. Когда он получает последнюю часть сообщения, он передает завершенное сообщение.

  procedure TBTMsgHandlerThread.Execute();
var
  msg: TBtMsgRec;
  NewMsg: Boolean;
  i: Integer;
begin
  inherited;
  while not terminated do
  begin
    if (FRawMsgList.Count > 0) then
    begin
      msg := parseMsg(FRawMsgList.First);
      FRawMsgList.Delete(0);
      NewMsg := True;

      for i := 0 to FParsedMsgList.Count - 1 do
        if (msg.FGuid = FParsedMsgList[i].FGuid) then
        begin
          NewMsg := false;
          msg.Data := FParsedMsgList[i].Data + msg.Data;
          FParsedMsgList.Delete(i);
          if (msg.part = msg.max) then
            Synchronize(
              procedure
              begin
                if assigned(FBluetoothOnMsgReady) then
                  FBluetoothOnMsgReady(msg);
              end)
          else
            FParsedMsgList.add(msg);
          break;
        end;
      if NewMsg then
        if msg.max = 1 then
          Synchronize(
            procedure
            begin
              if assigned(FBluetoothOnMsgReady) then
                FBluetoothOnMsgReady(msg);
            end)
        else
          FParsedMsgList.add(msg);
    end;
  end;
end;

Краткое описание проблемы:

Отправка (отслеживается с помощью обратного вызова, который информирует меня о состоянии отправляемого сообщения) и объединяющая часть работает нормально, но некоторые сообщения не достигают своей цели. Я не могу видеть шаблон и, к сожалению, нет сообщений об ошибках.

Я думаю, что проблема заключается в моем использовании потоков. Моя теория состоит в том, что поток получателя так или иначе пропускает некоторые сообщения. Поэтому я попытался повысить приоритет принимающего потока, а также изменить время ожидания потоков. Хотя я думаю, что это не очень хорошая идея использовать сон в потоках. Но я впервые работаю с потоками, и большинство примеров кода для связи по Bluetooth используют его.

Edit:

procedure TBTMsgHandlerThread.addRawMsg(ARawMsg: String);
begin
  Synchronize(
    procedure
    begin
      FRawMsgList.add(ARawMsg);
    end);
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...