菜鸟教程小白 发表于 2022-12-11 22:18:43

ios - 无法发现服务


                                            <p><p>我是我的 Xamarin.iOS 应用程序,我正在宣传服务 UUID,并使用 BLE 同时扫描所有服务 UUID。这是我的代码:</p>

<pre><code>
public class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
    private CBCentralManager _cbCentralManager;
    private CBPeripheralManager _cbPeripheralManager;
    private System.Threading.Timer _timer = null;

    // class-level declarations

    public override UIWindow Window
    {
      get;
      set;
    }

    public event EventHandler&lt;string&gt; Log;

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
      global::Xamarin.Forms.Forms.Init();
      LoadApplication(new MobileDemo.App());

      if (_cbCentralManager == null)
      {
            _cbCentralManager = new CBCentralManager();
      }

      if(_cbPeripheralManager == null)
      {
            _cbPeripheralManager = new CBPeripheralManager();
      }

      _cbPeripheralManager.AdvertisingStarted -= CBPeripheralManager_AdvertisingStarted;
      _cbPeripheralManager.AdvertisingStarted += CBPeripheralManager_AdvertisingStarted;

      _cbCentralManager.DiscoveredPeripheral -= CBCentralManager_DiscoveredPeripheral;
      _cbCentralManager.DiscoveredPeripheral += CBCentralManager_DiscoveredPeripheral;

      _cbCentralManager.UpdatedState -= CBCentralManager_UpdatedState;
      _cbCentralManager.UpdatedState += CBCentralManager_UpdatedState;

      return base.FinishedLaunching(application, launchOptions);
    }

    private void CBPeripheralManager_AdvertisingStarted(object sender, NSErrorEventArgs e)
    {
      Log?.Invoke(null, $&#34;advertising started: {e?.Error?.ToString()}&#34;);
    }

    private void CBCentralManager_UpdatedState(object sender, EventArgs e)
    {
      Log?.Invoke(null, $&#34;CB update state: {_cbCentralManager.State}&#34;);

      if (_cbCentralManager.State == CBCentralManagerState.PoweredOn)
      {
            _timer = null;
            _timer = new System.Threading.Timer((obj) =&gt;
            {
                Scan();
            },
            null, 1000, 5000);


            // Wait for 2 seconds before start advertising, or it won&#39;t work sometimes
            System.Threading.Timer _advertise = null;
            _advertise = new System.Threading.Timer((obj) =&gt;
            {
                StartAdvertisingOptions advOptions = new StartAdvertisingOptions
                {
                  ServicesUUID = new CBUUID[] { CBUUID.FromString(&#34;12345678-1111-1111-1111-000000000000&#34;)}
                };

                _cbPeripheralManager.StartAdvertising(advOptions);

                _advertise.Dispose();
                _advertise = null;
            },
            null, 2000, 0);
      }
      else
      {
            _cbCentralManager.StopScan();
            _cbPeripheralManager.StopAdvertising();
      }
    }

    private async void Scan()
    {
      Log?.Invoke(null, $&#34;Scanning...&#34;);

      _cbCentralManager.ScanForPeripherals(new CBUUID); // Do NOT pass null to this method. It won&#39;t work. Pass empty array instead

      await Task.Delay(2000);

      Log?.Invoke(null, $&#34;Stoping scan...&#34;);

      _cbCentralManager.StopScan();
    }

    private void CBCentralManager_DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs e)
    {
      Log?.Invoke(null, $&#34;Peripheral discovered&#34;);
      GetService(e.Peripheral);
    }

    private async Task WaitForTaskWithTimeout(Task task, int timeout)
    {
      await Task.WhenAny(task, Task.Delay(timeout));
      if (!task.IsCompleted)
      {
            throw new TimeoutException();
      }
    }

    public async Task GetService(CBPeripheral peripheral)
    {
      var service = this.GetServiceIfDiscovered(peripheral);
      if (service != null)
      {
            Log?.Invoke(null, $&#34;service found&#34;);
            return;
      }

      var taskCompletion = new TaskCompletionSource&lt;bool&gt;();
      var task = taskCompletion.Task;
      EventHandler&lt;NSErrorEventArgs&gt; handler = (s, e) =&gt;
      {
            service = this.GetServiceIfDiscovered(peripheral);

            if (service != null)
            {
                Log?.Invoke(null, $&#34;service found&#34;);
                taskCompletion.SetResult(true);
            }
            else
            {
                Log?.Invoke(null, $&#34;no service&#34;);
            }
      };

      try
      {
            peripheral.DiscoveredService += handler;
            peripheral.DiscoverServices();
            await this.WaitForTaskWithTimeout(task, 2000);
            service = this.GetServiceIfDiscovered(peripheral);

            if (service != null)
            {
                Log?.Invoke(null, $&#34;service found&#34;);
            }
            else
            {
                Log?.Invoke(null, $&#34;no service&#34;);
            }
      }
      finally
      {
            peripheral.DiscoveredService -= handler;
      }
    }

    public CBService GetServiceIfDiscovered(CBPeripheral peripheral)
    {
      return peripheral.Services?.FirstOrDefault();
    }      
}
</code></pre>

<p>我可以发现外围设备,但我的 <code>DiscoveredService</code> 处理程序永远不会被调用。我知道广告有效,因为我可以在同一应用程序的 Android 版本(不同的实现)上发现服务 UUID。但我无法在另一台 iOS 设备上发现该服务。我做错了什么?</p>

<p><strong>编辑</strong></p>

<p>因为我发现在发现它的服务之前我实际上必须连接到设备,所以我编写了一个管理器类来帮助连接和发现我正在从不同设备宣传的服务 UUID:</p>

<pre><code>public class BleServiceManager
{
    private readonly CBCentralManager _cbCentralManager;

    private bool _isGettingService;
    private bool _isConnectingToPeripheral;
    private Queue&lt;CBPeripheral&gt; _disconnectedPeripherals = new Queue&lt;CBPeripheral&gt;();
    private Queue&lt;CBPeripheral&gt; _connectedPeripherals = new Queue&lt;CBPeripheral&gt;();

    public event EventHandler&lt;string&gt; Log;
    public event EventHandler&lt;string&gt; FoundMyService;

    public BleServiceManager(CBCentralManager cbCentralManager)
    {
      _cbCentralManager = cbCentralManager;
    }

    public void FindServiceForPeripheral(CBPeripheral peripheral)
    {
      if (peripheral.State == CBPeripheralState.Disconnected)
      {
            _disconnectedPeripherals.Enqueue(peripheral);

            if (!_isConnectingToPeripheral)
            {
                ConnectToNextPeripheral();
            }
      }
    }

    private void ConnectToNextPeripheral()
    {
      if (_disconnectedPeripherals.Any())
      {
            _isConnectingToPeripheral = true;

            var p = _disconnectedPeripherals.Dequeue();

            if (p.State == CBPeripheralState.Disconnected)
            {
                ConnectTo(p);
            }
            else
            {
                _isConnectingToPeripheral = false;
            }
      }
      else
      {
            _isConnectingToPeripheral = false;
      }
    }

    private async Task ConnectTo(CBPeripheral peripheral)
    {
      var taskCompletion = new TaskCompletionSource&lt;bool&gt;();
      var task = taskCompletion.Task;
      EventHandler&lt;CBPeripheralEventArgs&gt; connectedHandler = (s, e) =&gt;
      {               
            if (e.Peripheral?.State == CBPeripheralState.Connected &amp;&amp; peripheral.Identifier?.ToString() == e.Peripheral.Identifier?.ToString())
            {
                _connectedPeripherals.Enqueue(peripheral);

                taskCompletion.SetResult(true);
            }
      };

      try
      {
            _cbCentralManager.ConnectedPeripheral += connectedHandler;
            _cbCentralManager.ConnectPeripheral(peripheral);
            await this.WaitForTaskWithTimeout(task, 2000);
            Log?.Invoke(null, $&#34;Bluetooth device connected = {peripheral.Name}&#34;);

            if (!_isGettingService)
            {
                DiscoverServicesOnNextConnectedPeripheral();
            }
      }
      catch (TimeoutException e)
      {
            Disconnect(peripheral);
      }
      finally
      {
            _cbCentralManager.ConnectedPeripheral -= connectedHandler;
            ConnectToNextPeripheral();
      }
    }

    private void DiscoverServicesOnNextConnectedPeripheral()
    {
      if (_connectedPeripherals.Any())
      {
            _isGettingService = true;

            var p = _connectedPeripherals.Dequeue();

            GetService(p);
      }
      else
      {
            _isGettingService = false;
      }
    }

    private async Task GetService(CBPeripheral peripheral)
    {
      var service = this.GetServiceIfDiscovered(peripheral);
      if (service != null)
      {
            Log?.Invoke(null, $&#34;service found&#34;);
            Disconnect(peripheral);
            FoundMyService?.Invoke(null, service.UUID.Uuid);
            DiscoverServicesOnNextConnectedPeripheral();
            return;
      }

      var taskCompletion = new TaskCompletionSource&lt;bool&gt;();
      var task = taskCompletion.Task;
      EventHandler&lt;NSErrorEventArgs&gt; handler = (s, e) =&gt;
      {
            service = this.GetServiceIfDiscovered(peripheral);

            if (service != null)
            {
                Log?.Invoke(null, $&#34;service found&#34;);
                FoundMyService?.Invoke(null, service.UUID.Uuid);
                taskCompletion.SetResult(true);
            }
            else
            {
                Log?.Invoke(null, $&#34;no service&#34;);
            }
      };

      try
      {
            peripheral.DiscoveredService += handler;
            peripheral.DiscoverServices();
            await this.WaitForTaskWithTimeout(task, 10000);
            service = this.GetServiceIfDiscovered(peripheral);

            if (service != null)
            {
                Log?.Invoke(null, $&#34;service found&#34;);
                FoundMyService?.Invoke(null, service.UUID.Uuid);
            }
            else
            {
                Log?.Invoke(null, $&#34;no service&#34;);
            }
      }
      catch(TimeoutException e)
      {

      }
      finally
      {
            peripheral.DiscoveredService -= handler;
            Disconnect(peripheral);
            DiscoverServicesOnNextConnectedPeripheral();
      }
    }

    private CBService GetServiceIfDiscovered(CBPeripheral peripheral)
    {
      return peripheral.Services?.FirstOrDefault(x =&gt; x.UUID?.Uuid?.StartsWith(&#34;12345678&#34;) == true); // the service uuid that I am advertising starts with 12345678
    }

    private void Disconnect(CBPeripheral peripheral)
    {
      _cbCentralManager.CancelPeripheralConnection(peripheral);
    }

    private async Task WaitForTaskWithTimeout(Task task, int timeout)
    {
      await Task.WhenAny(task, Task.Delay(timeout));
      if (!task.IsCompleted)
      {
            throw new TimeoutException();
      }
    }
}
</code></pre>

<p>一发现外围设备,我就会从我的 <code>AppDelegate</code> 调用 <code>FindServiceForPeripheral</code>。它是事件驱动的,因此 <code>GetService</code> 一次调用的次数永远不会超过 1 个。它会找到其他服务(例如电池),但找不到我宣传的服务。</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>实际上比我想象的要简单得多。通告服务 UUID:</p>

<pre><code>StartAdvertisingOptions advOptions = new StartAdvertisingOptions
{
ServicesUUID = new CBUUID[] { CBUUID.FromString(&#34;yourUuidHere&#34;) }
};

_cbPeripheralManager.StartAdvertising(advOptions);
</code></pre>

<p>要在另一台设备上发现相同的 UUID:</p>

<pre><code>_cbCentralManager.DiscoveredPeripheral += CBCentralManager_DiscoveredPeripheral;

private void CBCentralManager_DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs e)
{
    var key = new NSString(&#34;kCBAdvDataServiceUUIDs&#34;);

    foreach(var x in e.AdvertisementData.Keys)
    {
      if(x is NSString &amp;&amp; (NSString)x == key)
      {
            var y = e.AdvertisementData.ValueForKey((NSString)x);

            if(y.ToString().Contains(&#34;yourUuidHere&#34;))
            {
                  // found it
            }
      }
    }
}
</code></pre>

<p>我猜 <code>DiscoverServices()</code> 调用是为了找到 <a href="https://www.bluetooth.com/specifications/gatt/services" rel="noreferrer noopener nofollow">GATT services</a> ,而不是你自己的广告。</p></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 无法发现服务,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/54932536/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/54932536/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 无法发现服务