iOS-Performing Common Peripheral Role Tasks(API Reference)the fourth part

本文详细介绍了如何在iOS应用中实现蓝牙低能量(BLE)的peripheral角色,包括启动peripheral管理者、设置services和characteristics、发布services、广播services以及响应来自central的读写请求等内容。

Performing Common Peripheral Role Tasks


上一个章节,你学习了怎么执行最普通的蓝牙低能量任务从central 端。在这个章节,你学习在peripheral端怎么执行最普通的蓝牙低能昂任务。下面的代码演示你开发你的app实现peripheral角色在你本地的设备。规制,你将学习什么:

  • 开启一个peripheral管理者对象
  • 在你的本地的peripheral 搭建services和characteristic
  • 发布你的services和characteristics到你的设备的本地数据库
  • 广播你的service
  • 响应来自一个连接的central 读取和写入请求
    代码例子你会发现在这个章节是简单和抽象的,你可能需要进行适当的更改在你的实际的app中。更高级的实现central 角色的相关主题–包括提示,技巧,和最佳的练习在之后的章节。

Starting Up a Peripheral Manager


第一步在你的本地设备alloc和init一个peripheral manager实例(代表CBPeripheralManager对象)实现peripheral角色。开启你的peripheral管理者通过调用CBPeripheralManager类的initWithDelegate:queue:options:,像这样:

myPeripheralManager =
        [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];

在这个例子中,self是作为delegate,接收任何的peripheral角色的事件。当你指定dispathc queue为nil,peripheral管理者分配peripheral角色事件使用main queue。

当你创建一个peripheral管理者,central管理者调用delegate中的peripheralManagerDidUpdateState::方法。你必须实现这个delegate方法去确保是支持蓝牙低能量和在central设备中是可获得的。关于更多怎么实现delegate方法的信息,看CBPeripheralManagerDelegate Protocol Reference
.


Setting Up Your Services and Characteristics


如图1-7所示,一个本地的peripheral的数据库services和characteristics是组织树状的方法 你必须组织他们在这个树状方式设置本地peripheral的services和characteristics。你的执行这些任务的第一步是了解services和识别characteristics。


Services and Characteristics Are Identified by UUIDs


peripheral的services和characteristics的标识是128位蓝牙指定UUIDs,代表Core Bluetooth framework中的CBUUID对象。虽然并不是所有的UUIDs识别service或者预定义的characteristic通过Special Interest Group(SIG),蓝牙SIG定义和发布一些常用的uuid,为方便缩短16位。例如,蓝牙SIG有预定义的标识一个心率services的16位UUID为180 d。UUID是缩短从它的等效128位的UUID,0000180 d - 0000 - 1000 - 8000 - 00805 - f9b34fb,基于蓝牙UUID定义在蓝牙4.0规范中,卷3 F部分,3.2.1节。

当开发你的app的时候,CBUUID提供了工厂方法使用它处理长的UUIDs更容易。例如,在你的代码中而不是通过字符串表示的心率services的128位UUID,您可以简单地使用UUIDWithString方法创建一个CBUUID对象从service的预定义的16位UUID,像这样:

 CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];

当你从一个预定义的16位创建一个CBUUID对象时,Core Bluetooth预填充剩余的128位UUID和蓝牙基本的UUID。


Create Your Own UUIDs for Custom Services and Characteristics


你可能通过预定义的蓝牙UUID没有识别services和characteristics。如果你这样做,你需要生成自己的128位uuid来识别它们
使用命令行uuidgen轻松的创建128位UUID。首先打开一个终端窗口。接下来,为每个service和characteristic,您需要识别UUID,在命令行中输入uuidgen,接收一个惟一的ASCII字符串形式的128位值的连字符,如以下例子:

$ uuidgen
71DA3FD1-7E10-41C1-B16F-4430B506CDE7片

你你可以使用这个UUID创建一个CBUUID对象通过UUIDWithString方法,像这样:

CBUUID *myCustomServiceUUID =
        [CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"];

Build Your Tree of Services and Characteristics


你你在你的services和characteristics有UUID之后,您可以创建可变的services和characteristics和组织在上述树状方式。例如,如果你有characteristic的UUID,您可以创建一个可变characteristics通过调用
CBMutableCharacteristic 类的
initWithType:properties:value:permissions:方法,像这样:

myCharacteristic =
        [[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID
         properties:CBCharacteristicPropertyRead
         value:myValue permissions:CBAttributePermissionsReadable];

你当你创建了一个可变的characteristic,你设置它的属性,值,和权限。属性和权限设置好后,除此之外,characteristic的值是否可读的还是可写的,以及连接central是否可以订阅characteristic的值。在这个例子中,characteristic的值被设置为被连接central可读。更多信息支持的属性和权限范围可变的characteristic,看到CBMutableCharacteristic Class Reference

Note:如果你指定为characteristic指定一个值,这个值是缓存的和它的属性和权限是设置成可读的。因此,如果你需要一个可以写的characteristic的值,或者如果你期望值变化期间characteristic属于发布的service的生命周期,您必须指定值是nil。下面这种方法保证了所请求的值是动态和peripheral管理者只要peripheral收到一个读或写请求连接central。

现在您已经创建了一个可变的characteristic,您可以创建一个可变的service关联characteristic。要做到这一点,调用
CBMutableService 类的
initWithType:primary:方法:

myService = [[CBMutableService alloc] initWithType:myServiceUUID primary:YES];

在这个例子中,第二个参数设置为YES,表明service是主要与次要的。主要service描述设备的主要功能和可以包含另一个service(引用)。次要服务描述服务相关的只有在上下文中引用的另一个服务。例如,心率监视器的主要service可能暴露心率监测器的心率传感器的数据,而第二个servuce可能暴露传感器的电池数据。
你创建一个服务之后,你可以设置这个service的charactertics的数组,像这样:

myService.characteristics = @[myCharacteristic];


Publishing Your Services and Characteristics


你已经构建树状services和characteristics之后,下一步是实现peripheral角色,在你的本地设备发布他们到设备的services和characteristics数据。这是是非常容易的使用Core Bluetooth framework。你调用CBPeripheralManager类的addService:方法,像这样:

[myPeripheralManager addService:myService];

当你调用这个方法发布你的services的时候,peripheral管理者调用它的代理对象方法peripheralManager:didAddService:error:。如果一个错误发生和你的services不能发布,实现这个代理方法获取错误的原因,以下例子演示:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
            didAddService:(CBService *)service
                    error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error publishing service: %@", [error localizedDescription]);
    }
    ...

>Note:发布一个service和任何它相关的characterictics到peripheral的数据库,service是缓存的,你可以不再对其进行更改。


Advertising Your Services


当你已经发布你的services和characteristics到你的设备的services和characteristics数据库,你准备开始广播他们到任何可能监听到的central.下面的例子,你可以广播一些services通过调用CBPeripheralManagerstartADvertising:方法,通过在一个字典中实例化广播数据:

   [myPeripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey :
        @[myFirstService.UUID, mySecondService.UUID] }];

在这个例子中,字典中的key是唯一的, CBAdvertisementDataServiceUUIDsKey
,期望一个代表你想广播的services的UUID的CBUUID对象数组作为一个值,你可以去CBCentralManagerDelegate Protocol Reference的Advertisement Data Retrieval Keys的常量描述中寻找广播数据key的细节。只有两个key支持peripheral管理者对象:
CBAdvertisementDataLocalNameKeyCBAdvertisementDataServiceUUIDsKey
当你在你的本地peripheral开始广播一些数据,peripheral管理者调用他的代理对象方法:peripheralManagerDidStartAdvertising:error:
如果发生一个错误和你的service是不能广播的,实现这个他的代理方法访问这个错误的原因,像这样:

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
                                       error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error advertising: %@", [error localizedDescription]);
    }
    ...

Note:数据的广播是“best effort”的基础上完成的,因为空间有限,同时可能会有多个app的广播。有关更多信息,请参见startAdvertising:的讨论:方法在CBPeripheralManager Class Reference
广播的行为也影响了当你的应用程序在后台。在下一章中讨论过这个问题,核心蓝牙iOS应用程序的后台处理。

一旦你开始广播数据,远程central能发现和初始化一个连接和你。


Responding to Read and Write Requests from a Central


你连接一个或者多个远程central之后,你可能从它们那里开始接受读取或者写入请求。当你这样做确保在一个适当的方法响应那些请求。下面的例子描述了怎样处理这样的请求。

当一个连接的central请求读取你的characteristics中的一个值,peripheral管理者调用它的代理对象方法:peripheralManager:didReceiveReadRequest:,代理方法将该请求以CBATTRequest对象的形式传递给你,它有许多的属性,您可以使用它们来完成请求。

例如:当你接受一个简单的请求读取一个characteristic的值,你接收的代理方法中的CBATTRequest参数的属性可用于确保在你的设备数据库的characteristic匹配远程central指定的读取请求,像这样:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
    didReceiveReadRequest:(CBATTRequest *)request {
 
    if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
        ...

如果characteristic的UUID匹配,下一步是确保读取请求不要求读取索引的范围以外的位置的characteristic的值。如以下示例所示,您可以使用一个CBATTRequest对象的偏移属性,以确保外的读取请求不是试图读取合适的范围以外:

if (request.offset > myCharacteristic.value.length) {
        [myPeripheralManager respondToRequest:request
            withResult:CBATTErrorInvalidOffset];
        return;
    }

假设请求的偏移量是正确的,现在设置请求的characteristic属性的值(默认的值为nil)到你在你的本地peripheral创建的characteristic的值,考虑到读取请求的偏移量:

request.value = [myCharacteristic.value
        subdataWithRange:NSMakeRange(request.offset,
        myCharacteristic.value.length - request.offset)];

设置值后,响应远程central表示请求成功完成。通过调用CBPeripheralManager类的respondToRequest:withResult:方法,通过请求(其值更新)和请求的结果,是这样的:

[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    ...

调用respondToRequest:withResult:方法每次都会调用代理方法这里写链接内容peripheralManager:didReceiveReadRequest:

Note:如果characteristic的UUID没有匹配,或者如果读取因为任何的其他原因没有完成,你将不会完成这个请求。反而,你将立即调用respondToRequest:withResult:方法和提供一个失败的原因。对可能结果的列表你可以指定,看 Core Bluetooth Constants Reference.CBATTError Constants

处理写请求从一个连接central也很简单。连接central发送写的一个或多个characteristic的值请求时,peripheral管理者调用它的的代理对象方法 peripheralManager:didReceiveWriteRequests:。同一时间,代理方法包含一个或多个CBATTRequest对象数组的形式向你发送请求,每个代表一个写请求。确保写请求后完成后,,您可以编写characteristic的价值,像这样:

  myCharacteristic.value = request.value;

虽然上面的例子不能证明这一点,一定要考虑到请求的偏移量属性,当你写入你的characteristic的值的时候。
就像你响应一个读取请求,调用respondToRequest:withResult:方法同一时间会调用代理方法 peripheralManager:didReceiveWriteRequests:respondToRequest:withResult: 的第一个参数希望一个单一的CBATTRequest对象,即使你已经从peripheralManager:didReceiveWriteRequests: 代理方法接收了一个包含多个CBATTRequest对象的数组。你应该通过数组第一个请求,像这样:

 [myPeripheralManager respondToRequest:[requests objectAtIndex:0]
        withResult:CBATTErrorSuccess];

Note:>对待多个请求作为单个请求,如果任何个人请求不能完成,你不应该满足其中任何一个。相反,立即调用respondToRequest:withResult:方法并提供结果,指出失败的原因。


Sending Updated Characteristic Values to Subscribed Centrals


通常,连接central将订阅一个或者多个你的characteristic值,作为在 Subscribing to a Characteristic’s Value的描述,当他们这样做,你是负责发送通知,当他们订阅characteristic的值改变。下面的例子描述。
当一个连接的central订阅你的characteristic的其中一个值的时候,periperhal管理者调用它的代理对象方法 peripheralManager:central:didSubscribeToCharacteristic:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
                  central:(CBCentral *)central
didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
 
    NSLog(@"Central subscribed to characteristic %@", characteristic);
    ...

使用上面的代理方法作为开始返送central更新值的提示。

接下来,得到characteristic的更新的值,和发送它到central 通过调用
CBPeripheralManager类的
updateValue:forCharacteristic:onSubscribedCentrals:

NSData *updatedValue = // fetch the characteristic's new value
    BOOL didSendValue = [myPeripheralManager updateValue:updatedValue
        forCharacteristic:characteristic onSubscribedCentrals:nil];

当你调用这个方法发送更新的characteristic的值到订阅的centrals,你可以指定哪个central更新最后一个参数。在上面的例子中,如果你指定为nil,所有的连接和订阅的centrald是更新的(包括任何连接的central但没有订阅的)

updateValue:forCharacteristic:onSubscribedCentrals: 方法返回一个Boolean值声明发送订阅是否成功。如果底层的队列用于发送更新后的值是完成的,该方法返回NO。periperhal管理者然后调用它的的代理对象方法 peripheralManagerIsReadyToUpdateSubscribers:,当更多的空间传输队列是可获得的。然后您可以实现这个delegate方法来重新发送这个值,再使用
updateValue:forCharacteristic:onSubscribedCentrals:方法。

Note:使用通知发送一个单一的数据包到订阅的central.当您更新订阅的central,你应该发送整个更新值在单一的通知,通过调用
updateValue:forCharacteristic:onSubscribedCentrals:方法一次。
根据characterisric的值的大小,并不是所有的数据可以传输通过通知。如果发生这种情况,这种情况应该在central端通过调用CBPeripheral类的readValueForCharacteristic:方法c处理,检索整个价值。

<think>我们正在解决CentOS安装过程中卡在"performingpost-installationsetuptasks"的问题。这个问题通常发生在安装完成后重启前,系统进行一些初始化设置时卡住。可能的原因包括:硬件兼容性问题(尤其是显卡驱动)、系统服务启动失败、文件系统问题等。解决思路:1.尝试等待:有时这个过程需要较长时间(尤其是大硬盘或旧硬件),建议等待30分钟以上。2.检查安装介质:使用校验和验证ISO的完整性,并确保刻录到U盘或光盘时没有错误。3.使用基本图形模式:在安装引导时,修改启动参数,加上`nomodeset`(针对显卡问题)。4.尝试文本模式安装:在安装引导时,修改启动参数为`text`(但CentOS7以后文本安装模式不完整,通常不推荐)。5.检查硬件:特别是新硬件,可能需要更新内核或使用较新的CentOS版本(如CentOSStream)或Fedora。6.检查存储设备:如使用RAID或特殊文件系统,可能需要额外驱动。7.禁用SELinux:在安装引导时,加上`selinux=0`,但安装完成后需要谨慎启用。8.检查日志:在安装界面可以切换至tty(Ctrl+Alt+F2~F6)查看系统日志,如`/var/log/messages`,`/var/log/anaconda/*.log`等。具体步骤:方法1:使用`nomodeset`参数1.在安装引导界面(出现第一个菜单时),按`e`键编辑启动参数。2.找到以`linux`开头的那一行,在行尾添加`nomodeset`(注意空格)。3.按Ctrl+X启动,然后重新安装。方法2:禁用SELinux1.同样在编辑启动参数时,在`linux`行尾添加`selinux=0`。方法3:检查日志1.当安装程序卡住时,尝试按Ctrl+Alt+F2切换到tty2,然后以root登录(无需密码)。2.查看日志文件,如`/var/log/messages`,`/var/log/anaconda/journal.log`等。3.通过日志定位问题。方法4:最小化安装1.在安装类型选择时,选择最小安装(MinimalInstall),减少安装的包数量,降低出错概率。方法5:更新安装源1.如果使用网络安装,尝试更换镜像源。如果以上方法都不能解决,可能需要考虑硬件故障(如内存、硬盘)或尝试其他Linux发行版。注意:在安装过程中,如果卡在某个点,有时是由于后台服务(如NetworkManager,firewalld)启动慢,可以尝试关闭这些服务,但安装程序通常不允许这样做。引用说明:根据Linux社区常见问题解决方案[^1],卡在安装后任务通常与显卡驱动或服务启动有关。相关问题:1.如何验证CentOS安装介质的完整性?2.在CentOS安装过程中如何查看系统日志?3.如何解决Linux安装过程中的显卡驱动问题?4.CentOS最小安装包括哪些软件包?5.如何为CentOS安装添加额外的驱动程序?</think>### CentOS安装卡在“执行安装后设置任务”的解决方案 当CentOS安装卡在"performing post-installation setup tasks"阶段时,通常与系统服务初始化、硬件兼容性或配置冲突有关。以下是逐步解决方法: #### 1. **基础排查步骤** - **等待观察**:首次安装时此阶段可能持续10-30分钟(尤其旧硬件),建议等待至少40分钟[^1]。 - **检查安装介质**: ```bash # 在安装启动菜单选择"Verify and Install" ``` 确认ISO的SHA256校验值:`sha256sum CentOS-*.iso` - **安全模式启动**:在GRUB菜单按`e`编辑启动参数,在`linux`行尾添加: ```bash systemd.unit=multi-user.target # 禁用图形服务 selinux=0 # 临时禁用SELinux ``` 按Ctrl+X启动 #### 2. **关键解决方案** - **禁用问题服务**(通过TTY终端): 1. 卡住时按`Ctrl+Alt+F2`切到TTY2 2. 登录后检查失败服务: ```bash journalctl -p 3 -xb # 查看ERROR级日志 systemctl list-units --failed # 列出失败服务 ``` 3. 禁用常见冲突服务: ```bash systemctl mask NetworkManager-wait-online.service systemctl mask firewalld.service ``` - **文件系统检查**: ```bash fsck -fy /dev/mapper/centos-root # 检查根分区 mount -o remount,rw / # 强制重新挂载为读写 ``` #### 3. **硬件相关处理** - **显卡驱动**: - NVIDIA显卡:启动参数添加`nouveau.modeset=0` - VMware虚拟机:添加`nomodeset`参数 - **磁盘控制器**: 在BIOS中切换SATA模式(AHCI→IDE或反之) - **内存检测**: 从安装U盘启动选择"Memory Test" #### 4. **高级修复** ```bash # 重建initramfs(需chroot) chroot /mnt/sysimage dracut -f --regenerate-all ``` ```bash # 修复GRUB grub2-install /dev/sda grub2-mkconfig -o /boot/grub2/grub.cfg ``` > **重要提示**:若使用RAID/LVM,检查`/etc/fstab`和`/etc/default/grub`配置。云平台安装需确认virtio驱动[^2]。 #### 预防措施 - 安装时选择**最小化安装**(Minimal Install) - 断开非必要外设(USB网卡/打印机) - 使用CentOS Stream版本获取更新驱动 --- ### 相关问题 1. 如何查看CentOS安装过程中的详细日志? 2. 安装后`systemd`服务启动失败的通用修复方法是什么? 3. 虚拟机环境中CentOS安装卡顿有哪些特殊优化方法? 4. 如何验证硬件兼容性?[^1] 5. 系统成功启动后如何重新启用SELinux?[^2] [^1]: Red Hat Enterprise Linux Installation Guide, Chapter 23. Troubleshooting [^2]: CentOS Wiki: Post-Installation Troubleshooting
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有头发的猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值