蓝牙指令超时执行超时是什么意思

ios之蓝牙开发 - 简书
ios之蓝牙开发
因工作的需要, 前段时间整理了一下IOS蓝牙通讯相关的东西,把整理的一个成果给大家分享一下。如果有不足的地方欢迎指正。
一、项目背景
简单介绍一下我所做的东西,设备目前有四个,分别是体脂秤、血压计、血糖仪、监护仪等。从名称大家应该知道这些是什么东西了。对,没错,这些设备主要是用来测量人体的一些 数据,诸如血压血糖,脂肪等等。通过这些数据来反应用户的身体健康状况。 通过蓝牙与iphone手机通讯。手机端的app通过发送不同的指令(通过蓝牙)控制相应的设备执行一些动作,比如读取设备上的数据,待用户测量完成后将用户的数据发送到服务器,然后返回用户的健康状态到手机上等。 话不多说,下面让我们开始吧。
二、IOS 蓝牙介绍
蓝牙协议本身经历了从1.0到4.0的升级演变,最新的4.0以其低功耗著称,所以一般也叫BLE(Bluetoothlow energy)。
ios中蓝牙有四个框架,其中两个支持与外设连接。一个是 ExternalAccessory。从ios3.0就开始支持,也是在iphone4s出来之前用的比较多的一种模式,但是它有个不好的地方,External Accessory需要拿到苹果公司的MFI认证。另一个框架则是本文要介绍的CoreBluetooth,在iphone4s开始支持,专门用于与BLE设备通讯(因为它的API都是基于BLE的)。这个不需要MFI,并且现在很多蓝牙设备都支持4.0,所以也是在IOS比较推荐的一种开发方法。
三、CoreBluetooth介绍
CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心设备。对应他们分别有一组相关的API和类,如下图所示:
CoreBluetooth的结构
如果你要编程的设备是central那么你大部分用到,反之亦然。在我们这个示例中,血压计、体脂秤等等是peripheral,我们的iphone手机是central,所以我将大部分使用上图中左边部分的类。使用peripheral编程的例子也有很多,比如像用一个ipad和一个iphone通讯,ipad可以认为是central,iphone端是peripheral,这种情况下在iphone端就要使用上图右边部分的类来开发了。
四 、服务和特征
有个概念有必要先说明一下。什么是服务和特征呢(service and characteristic)?
每个蓝牙4.0的设备都是通过服务和特征来展示自己的,一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征。特征是与外界交互的最小单位。比如说,一台蓝牙4.0设备,用特征A来描述自己的出厂信息,用特征B来与收发数据等。
服务和特征都是用UUID来唯一标识的,UUID的概念如果不清楚请自行google,国际蓝牙组织为一些很典型的设备(比如测量心跳和血压的设备)规定了标准的service UUID(特征的UUID比较多,这里就不列举了),如下:
BLE_UUID_ALERT_NOTIFICATION_SERVICE
BLE_UUID_BATTERY_SERVICE
BLE_UUID_BLOOD_PRESSURE_SERVICE
BLE_UUID_CURRENT_TIME_SERVICE
BLE_UUID_CYCLING_SPEED_AND_CADENCE
BLE_UUID_DEVICE_INFORMATION_SERVICE
BLE_UUID_GLUCOSE_SERVICE
BLE_UUID_HEALTH_THERMOMETER_SERVICE
BLE_UUID_HEART_RATE_SERVICE
BLE_UUID_HUMAN_INTERFACE_DEVICE_SERVICE
BLE_UUID_IMMEDIATE_ALERT_SERVICE
BLE_UUID_LINK_LOSS_SERVICE
BLE_UUID_NEXT_DST_CHANGE_SERVICE
BLE_UUID_PHONE_ALERT_STATUS_SERVICE
BLE_UUID_REFERENCE_TIME_UPDATE_SERVICE
BLE_UUID_RUNNING_SPEED_AND_CADENCE
BLE_UUID_SCAN_PARAMETERS_SERVICE
BLE_UUID_TX_POWER_SERVICE
BLE_UUID_CGM_SERVICE
当然还有很多设备并不在这个标准列表里,比如我用的这个体脂秤血压计等等。蓝牙设备硬件厂商通常都会提供他们的设备里面各个服务(service)和特征(characteristics)的功能,比如哪些是用来交互(读写),哪些可获取模块信息(只读)等。
五、实现细节
作为一个中心要实现完整的通讯,一般要经过这样几个步骤:
建立中心角色—扫描外设(discover)—连接外设(connect)—扫描外设中的服务和特征(discover)—与外设做数据交互(explore and interact)—断开连接(disconnect)。
1.建立中心角色
首先在我自己类的头文件中要包含CoreBluetooth的头文件,并继承两个协议&CBCentralManagerDelegate,CBPeripheralDelegate&,代码如下:
#import &CoreBluetooth/CoreBluetooth.h&
// 中心设备管理者
@property (nonatomic, strong) CBCentralManager *centerM
//在viewDidLoad中初始化中心设备并设置代理
self.centerManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
2.扫描外设(discover)
[self.centerManager scanForPeripheralsWithServices:nil options:options];
这个参数应该也是可以指定特定的peripheral的UUID,那么理论上这个central只会discover这个特定的设备,但是我实际测试发现,如果用特定的UUID传参根本找不到任何设备.可能是由于设备本身在的广播包有关
3.连接外设(connect)
当扫描到4.0的设备后,系统会通过回调函数告诉我们设备的信息,然后我们就可以连接相应的设备,代码如下:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
// _dicoveredPeripherals是用来存放已经扫描到的外部设备
// 如果发现了这个外围设备我就把它添加到这个数组里面
if(![_dicoveredPeripherals containsObject:peripheral])
[_dicoveredPeripherals addObject:peripheral];
NSLog(@"dicoveredPeripherals:%@", _dicoveredPeripherals);
//连接指定的设备
-(BOOL)connect:(CBPeripheral *)peripheral
NSLog(@"connect start");
_testPeripheral =
[self.centerManager connectPeripheral:peripheral
options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
//开一个定时器监控连接超时的情况
connectTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:@selector(connectTimeout:) userInfo:peripheral repeats:NO];
return (YES);
4.扫描外设中的服务和特征(discover)
同样的,当连接成功后,系统会通过回调函数告诉我们,然后我们就在这个回调里去扫描设备下所有的服务和特征,代码如下:
// 连接外设
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
[connectTimer invalidate];//停止时钟
NSLog(@"Did connect to peripheral: %@", peripheral);
_testPeripheral =
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
一个设备里的服务和特征往往比较多,大部分情况下我们只是关心其中几个,所以一般会在发现服务和特征的回调里去匹配我们关心那些,比如下面的代码:
// 发现外围设备中的服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
NSLog(@"didDiscoverServices");
if (error)
NSLog(@"Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
if ([self.delegate respondsToSelector:@selector(DidNotifyFailConnectService:withPeripheral:error:)])
[self.delegate DidNotifyFailConnectService:nil withPeripheral:nil error:nil];
// 遍历外围设备中的服务
for (CBService *service in peripheral.services)
// 如果发现是我们需要的服务,那我们就去扫描特征值
if ([service.UUID isEqual:[CBUUID UUIDWithString:UUIDSTR_ELECTRONIC_SCALE_SERVICE]])
NSLog(@"Service found with UUID: %@", service.UUID);
[peripheral discoverCharacteristics:nil forService:service];
// 根据服务找到对应的特征值
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
if (error)
NSLog(@"Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
if ([self.delegate respondsToSelector:@selector(DidNotifyFailConnectChar:withPeripheral:error:)])
[self.delegate DidNotifyFailConnectChar:nil withPeripheral:nil error:nil];
// 遍历服务中所有的特征值
for (CBCharacteristic *characteristic in service.characteristics)
// 找到我们需要的特征
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:UUIDSTR_SCALE]])
NSLog(@"Discovered read characteristics:%@ for service: %@", characteristic.UUID, service.UUID);
_readCharacteristic =//保存读的特征
if ([self.delegate respondsToSelector:@selector(DidFoundReadChar:)])
[self.delegate DidFoundReadChar:characteristic];
for (CBCharacteristic * characteristic in service.characteristics)
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:UUIDSTR_ISSC_TRANS_RX]])
NSLog(@"Discovered write characteristics:%@ for service: %@", characteristic.UUID, service.UUID);
_writeCharacteristic =//保存写的特征
if ([self.delegate respondsToSelector:@selector(DidFoundWriteChar:)])
[self.delegate DidFoundWriteChar:characteristic];
if ([self.delegate respondsToSelector:@selector(DidFoundCharacteristic:withPeripheral:error:)])
[self.delegate DidFoundCharacteristic:nil withPeripheral:nil error:nil];
相信你应该已经注意到了,回调函数都是以"did"开头的,这些函数不用你调用,达到条件后系统后自动调用。
5.与外设做数据交互(explore and interact)
发送数据很简单,我们可以封装一个如下的函数:
- (void)writeChar:(NSData *)data
[_testPeripheral writeValue:data forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithResponse];
_testPeripheral和_writeCharacteristic是前面我们保存的设备对象和可以读写的特征。
然后我们可以在外部调用它,比如当然我要测量体重时,先组好数据包,然后调用发送函数:
- (void)sendData {
NSData *data = _characteristic.
if (data) { //当有数据的时候, 才会对返回的数据进行处理,不然会抛异常
// 将NSData转化成byte
Byte *byte
= (Byte *)[data bytes];
// 取出其中用用的两位
= {byte[5],byte[6]};
// 在转化成NSData
NSData *adata = [[NSData alloc] initWithBytes:b length:sizeof(b)];
NSLog(@"adata = %@", adata);
// 转化成字符串
NSString *str = [adata ConvertToNSString];
TSLog(@"%@",str);
数据的读分为两种,一种是直接读(reading directly),另外一种是订阅(subscribe)。从名字也能基本理解两者的不同。实际使用中具体用一种要看具体的应用场景以及特征本身的属性。前一个好理解,特征本身的属性是指什么呢?特征有个properties字段(characteristic.properties),它是一个整型值,有如下几个定义:
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
比如说,你要交互的特征,它的properties的值是0x10,表示你只能用订阅的方式来接收数据。我这里是用订阅的方式,启动订阅的代码如下:
//监听设备
-(void)startSubscribe
[_testPeripheral setNotifyValue:YES forCharacteristic:_readCharacteristic];
当设备有数据返回时,同样是通过一个系统回调通知我,如下所示:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
if (error)
NSLog(@"Error updating value for characteristic %@ error: %@", characteristic.UUID, [error localizedDescription]);
if ([_mainMenuDelegate respondsToSelector:@selector(DidNotifyReadError:)])
[_mainMenuDelegate DidNotifyReadError:error];
[_recvData appendData:characteristic.value];
if ([_recvData length] &= 5)//已收到长度
unsigned charchar *buffer = (unsigned charchar *)[_recvData bytes];
int nLen = buffer[3]*256 + buffer[4];
if ([_recvData length] == (nLen+3+2+2))
//接收完毕,通知代理做事
if ([_mainMenuDelegate respondsToSelector:@selector(DidNotifyReadData)])
[_mainMenuDelegate DidNotifyReadData];
6 断开连接(disconnect)
这个比较简单,只需要一个API就行了,代码如下:
//主动断开设备
- (void)disConnect
if (_testPeripheral != nil)
NSLog(@"disConnect start");
[self.centerManager cancelPeripheralConnection:_testPeripheral];
至此一个简单的蓝牙开发流程就走完了。在这里补充一点如何判断蓝牙的开启和关闭状态。(不好意思,这个应该是写在开头才对。 不管啦,就写在这里吧)。直接上代码喽。
//中心服务器状态更新后调用此方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBCentralManagerStatePoweredOff:
// 如果是关闭状态就提示用户蓝牙是关闭的,请打开
_msg = BLE_OFF;
case CBCentralManagerStatePoweredOn:
_msg = BLE_ON;
// 蓝牙开启了,就可以扫描外部设备了.
//扫描外围设备
[central scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];
if(_msg!=nil&&_msg.length!=0) {
TSLog(@"_msg == %@",self.msg);
为了使交互更好,我添加了一个提示框UIAlertController。因为你要使用这些硬件与手机连接是需要开启蓝牙服务的。 当用户没有打开蓝牙的时候,引用提示用户“你的应用需要打开蓝牙” 。只有当手机的蓝牙开启后,才可以扫描连接等等。 这里当我们点击允许的时候,就跳转到系统的蓝牙设置里面。代码如下:
// 检查蓝牙是否开启
[self centralManagerDidUpdateState:_centerManager];
if (_centerManager.state == CBCentralManagerStatePoweredOff) {
// 弹出提示框
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:@"你的应用想要打开蓝牙" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
TSLog(@"拒绝");
// 拒绝不作任何处理
UIAlertAction *otherAction = [UIAlertAction actionWithTitle:@"允许" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
TSLog(@"允许");
// 允许用户打开蓝牙就会跳转到蓝牙设置页面
// 关于系统的各个服务设置的url会在结尾给出
NSURL *url = [NSURL URLWithString:@"prefs:root=Bluetooth"];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
[alertController addAction:cancelAction];
[alertController addAction:otherAction];
[self presentViewController:alertController animated:YES completion:nil];
ios系统中各种设置项的url链接
当然,这个只是一个蓝牙开发的简单的流程,如果有什么不足的地方还请各位朋友指正。 如果有什么好的建议,欢迎指出,我们可以相互交流,共同进步哈^0^ ~~
ios开发爱好者
GitHub地址:/VictDog工业物联网通讯专家!
认真做好每一件事,设计简单、可靠、价格合理的产品!
& 应用笔记
4G模块的AT指令发送超时怎么办?
内容主题:
发布时间:
  (USR-402tf)一般的AT命令超时时长为30秒,发送短信的超时时长为60秒。在发送超时后,可以使用AT命令中的AT来判断设备是否正常。
  问题分析:
  1)4G模块发送超时的AT命令+CGATT=1(附着)会执行4次重发,重发间隔15秒,最坏需要75秒才能回应最终响应;+CGATT不会被其它AT命令中断,即使在75秒超时返回ERROR后,终端还会继续重复尝试附着过程。
在4G模块发送+CGATT=1成功后,+CGACT=1(激活PDP)也会执行4次重发,重发间隔30秒,最坏需要150秒才能最终响应。+CGACT可以被中断,以及时响应用户的其它命令。建议的超时时间40-90秒,保证至少有一次PDP重发的机会。
+CGACT=1可以附带启动附着过程(如果还没有完成附着),这样最坏的情况下需要225秒,4G模块才能收到响应。建议的流程是首先发送+CGATT=1,成功后再发送+CGACT=1,这样可以灵活的分配超时时间,出现问题也方便定位。
  4)在+CGACT=1成功后,+ZGACT实际是个本地命令主要是通知OS网卡已经UP,
OS启动DHCP请求IP地址,DNS等。这个过程在30秒内可以完成。
  典型示例:
  当前的拨号流程是先 CGACT 激活,然后下发 ZGACT 拨号,设备经过 34S 拨号成功,但 GUI 已超时(20 秒)失败。后来将 PC
侧拨号超时设置为 40 秒。40S 的时间可以保证 TDG 在恶劣场景下可能有一次 PDP 激活重发的机会。 40 秒的时间与中移 G3 随 e 行的 30
秒接近,用户体验影响不大。CPE 情况不同,建议超时时间可以更长,否则中断了 PDP 激活,接下来还是要发送+CGACT=1 激活
PDP,不如让设备自动重发激活 PDP。
*为必须填写项
评论内容:*3237人阅读
背景:WINCE6.0平台开发的机器和蓝牙设备连接,如果蓝牙设备主动断开连接,机器端20s才能检测到,时间太长,是否可以修改
调查:http://affon.narod.ru/BT/bluetooth_app_c10.pdf&Write Link_Supervision_Timeout章节讲到HCI_Write_Link_Supervision_Timeout命令可以修改连接监控超时时间。
HCI_Write_Link_Supervision_Timeout命令在CE底层有找到,只是相关接口未导出到应用,自己试图导出,可以实现时间的修改。
注意:蓝牙时间相关的参数*0.625=我们想要的时间,例如传递5000ms,实际为3s多点 。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:280290次
积分:2533
积分:2533
排名:第13016名
原创:62篇
转载:55篇
评论:26条
(1)(2)(3)(2)(2)(3)(1)(1)(2)(1)(3)(2)(2)(4)(2)(3)(1)(9)(7)(2)(3)(1)(1)(1)(6)(7)(1)(1)(2)(9)(4)(4)(2)(2)(5)(16)工作问题(124)
http://blog.csdn.net/qingtiantianqing/article/details/
onServicesDiscovered 回调里不能直接执行 write /readDataFromCharacteristic() 或者 enableNotificationOfCharacteristic之类的,而要放到主线程里执行,如 handler.post( … );
如果发现连接上了,service也discover到了,但是始终不能触发onCharacteristicChanged的,一定要查找如下2个重要原因:&
1). 一定要gatt.setCharacteristicNotification(characteristic, enable);&
2). 如果设置了1).却还是发现没有触发,这个时候比较坑爹了,加上对此Characteristic的descriptor做indication Enable就应该可以了;
for(BluetoothGattDescriptor dp:characteristic().getDescriptors()) {&
dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);&
mBluetoothGatt().writeDescriptor(dp);&
3.. 不同的机型的discoverService到onServiceDiscovered之间的耗时长短不一,这会导致一个问题:如果蓝牙硬件设备支持离线传输,即有记忆功能,连接上之后多久发送之前的数据的问题。如果连接之上立即发送,那么手机端的onServiceDiscovered尚未触发,这样Characteristic的值就获取不了(因为你的service,Characteristic都尚未初始化好),从而导致失败。
解决的办法有3个:&
1)建立一套ACK机制,蓝牙硬件设备不断的广播,直到所有的数据都收到返回的ACK确认才不再广播即可。&
2)更好的办法是,当手机端onServiceDiscovered触发后,并且service,Characteristic都初始化好后,发送指令给蓝牙硬件设备(即writeCharacteristic)表示手机端已经准备好,可以发送数据给我了,蓝牙硬件设备收到后再发送数据,这样能很好的保证数据不丢失。&
3) 最好的办法是1)和 2)的结合,即发送准备好的指令,然后让智能硬件发送数据,然后在接收数据的过程中,使用ACK机制确保数据没有任何丢失。
4.. Read/Write Characteristic/Descriptor 等都是异步的,即立即返回,等待回调。因此如果手机底层自身如果没有做请求的同步顺序执行的话,那么当有很多请求几乎同时进行时,回调顺序是无法保证的。此时就造成错误,这也会导致很多蓝牙4.0不能兼容某些Android的原因,因此需要自己提供一套同步机制,如RequestQueue,来保证request&response
一个接一个高效有序的进行,即下一个request必须等到上一个request的response返回之后再执行。
5.. Read/Write Characteristic/Descriptor/RemoteRssi(),一般在不同的线程中回调。(除了onDescriptorWrite返回的线程与写入线程为同一个线程???)
BluetoothDevice.conncectGatt(),&
BluetoothGatt.connect(),&
BluetoothGatt.disconnect(),&
BluetoothGatt.discoverServices()&
最好都在主线程,否则会遇到很多意想不到的麻烦。
6.. BLE的特征一次读写最大长度20字节。
7.. Android手机会对连接过的BLE设备的Services进行缓存,若设备升级后Services等有改动,则程序会出现通讯失败。此时就得刷新缓存,反射调用BluetoothGatt类总的refresh()方法。
8.. startLeScan(UUID[], LeScanCallback)在Android 4.4及以下手机中似乎只支持16位的短UUID,不支持128位。
9.. connectGatt() 在某些三星手机上只能在UI线程调用。
10.. Android L 新API扫描设备换为 startScan(List, ScanSettings, ScanCallback)。
11.. Android M 必须拥有定位权限才能扫描BLE设备。
12.. 一个主设备(例如Android手机)可以同时连接多个从设备(一般为6个,例如智能硬件。超过就连接不上了),一个从设备只能被一个主设备连接,一旦从设备连接上主设备,就停止广播,断开连接则继续广播。在任何时刻都只能最多一个设备在尝试建立连接。如果同时对多个蓝牙设备发起建立Gatt连接请求。如果前面的设备连接失败了,则后面的设备请求会被永远阻塞住,不会有任何连接回调。所以建议:如果要对多个设备发起连接请求,最好是一个接一个的顺序同步请求管理。
13.. 任何出错,超时,用完就马上调用Gatt.disconnect(), Gatt.close()。
14.. 从bindService 到 onServiceConnected 这个回调花费时间较长, onServiceConnected 这个回调很可能在 MainActivity onResume之后才执行, 所以不要指望onResume里去执行扫描,因为此时serviceConnected 回调都尚未执行
15.. getBtAdapter().enable()是异步,立即返回,但从 off 到 on 的过程需要一个时间所以只能监听系统broadcast发出的intent里的state
16.. onCharacteristicWrite … 等等是指本机写数据指令已经成功发送出去,并且智能硬件已经处理完回应回来了,另外,当智能硬件端要求发送的指令有顺序的话,那么这边不能发送速度过快,即不能在onCharacteristicWrite里立即发送下一条指令。例如OAD/OTA等等,字节必须严格按照image的字节顺序发送出去。&
17.. 在writeCharacteristic时,若速度过快(例如在OAD时),会发现发送出去的数据有可能不是你自己真正发出去的,在onCharacteristicWrite里打印出可以确定。&
18.. App端的关于同一个UUID的2个指令不能同时发出去,这样会导致硬件端无法辨识,所以需要串行发送,即等其中一个发送回调成功之后,再进行下一个。
19.. 多次扫描蓝牙,在华为荣耀,魅族M3 NOTE 中有的机型,会发现多次断开–扫描–断开–扫描… 会扫描不到设备,此时需要在断开连接后,不能立即扫描,而是要先停止扫描后,过2秒再扫描才能扫描到设备。
20.. 扫描尽量不要放在主线程进行,可以放入子线程里。不然有些机型会出现 do too many work in main thread.
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:450210次
积分:6546
积分:6546
排名:第3197名
原创:191篇
转载:184篇
评论:46条
(8)(10)(8)(3)(1)(1)(2)(6)(1)(2)(4)(27)(6)(4)(2)(1)(1)(2)(1)(1)(1)(6)(2)(12)(4)(5)(1)(1)(2)(1)(6)(5)(9)(7)(2)(2)(8)(5)(7)(5)(5)(2)(7)(6)(13)(3)(3)(3)(1)(1)(3)(4)(1)(11)(2)(7)(4)(1)(4)(1)(2)(1)(6)(3)(5)(3)(2)(3)(9)(1)(1)(5)(15)(7)(1)(13)(7)(4)(1)(1)(1)(3)(14)(4)(2)

我要回帖

更多关于 mysql 执行超时设置 的文章

 

随机推荐