SendRequest 有时候会出现超时现象怎么解决

如题所述

第1个回答  2016-12-27
我有一个输入的流和输出流的蓝牙连接配件
我想要实现以下目标:
写入数据到 outputStream 等待,直到收到对题目: 下面的数据或直到 10 秒传递如果题目: 下面数据到达其他返回数据返回零
我试着所以执行此类似:
- (APDUResponse *)sendCommandAndWaitForResponse:(NSData *)request {
APDUResponse * result;
if (!deviceIsBusy && request != Nil) {
deviceIsBusy = YES;
timedOut = NO;
responseReceived = NO;
if ([[mySes outputStream] hasSpaceAvailable]) {
[NSThread detachNewThreadSelector:@selector(startTimeout) toTarget:self withObject:nil];
[[mySes outputStream] write:[request bytes] maxLength:[request length]];
while (!timedOut && !responseReceived) {
sleep(2);
NSLog(@"tick");
}
if (responseReceived && response !=nil) {
result = response;
response = nil;
}
[myTimer invalidate];
myTimer = nil;
}
}
deviceIsBusy = NO;
return result;
}

- (void) startTimeout {
NSLog(@"start Timeout");
myTimer = [NSTimer timerWithTimeInterval:10.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
}

- (void)timerFireMethod:(NSTimer *)timer {
NSLog(@"fired");
timedOut = YES;
}

- (void)stream:(NSStream*)stream handleEvent:(NSStreamEvent)streamEvent
{
switch (streamEvent)
{
case NSStreamEventHasBytesAvailable:
// Process the incoming stream data.
if(stream == [mySes inputStream])
{
uint8_t buf[1024];
unsigned int len = 0;
len = [[mySes inputStream] read:buf maxLength:1024];
if(len) {
_data = [[NSMutableData alloc] init];
[_data appendBytes:(const void *)buf length:len];
NSLog(@"Response: %@", [_data description]);
response = [[APDUResponse alloc] initWithData:_data];
responseReceived = YES;
} else {
NSLog(@"no buffer!");
}
}
break;
... //code not relevant
}
}

所以理论是有 NSTimer 会设置一个布尔值时它射击,然后 handleEvent 委托方法也有设置另一个 boolean 类型的值如果收到的数据的一个单独的线程上运行。在方法中,我们有一段时间睡一觉,当这些 bool 之一设置时停止循环。
我遇到的问题在 '超时的情况下' 我的 timerFireMethod 不是是越来越叫。我的直觉是我是不实际正确设置计时器在一个单独的线程上。
任何人都可以看看究竟怎么在这里或建议的上述要求的更好实施吗?
解决方法 1:
而是施加不当同步方法本质上异步的问题,使您的方法 sendCommandAndWaitForResponse 异步。
它是可能要包装的"流写入"任务为异步操作/任务/方法。例如,你可能最终与并发子类的 NSOperation 与下面的接口:
typedef void (^DataToStreamCopier_completion_t)(id result);

@interface DataToStreamCopier : NSOperation

- (id) initWithData:(NSData*)sourceData
destinationStream:(NSOutputStream*)destinationStream
completion:(DataToStreamCopier_completion_t)completionHandler;

@property (nonatomic) NSThread* workerThread;
@property (nonatomic, copy) NSString* runLoopMode;
@property (atomic, readonly) long long totalBytesCopied;

// NSOperation
- (void) start;
- (void) cancel;
@property (nonatomic, readonly) BOOL isCancelled;
@property (nonatomic, readonly) BOOL isExecuting;
@property (nonatomic, readonly) BOOL isFinished;

@end

您可以实现"超时"功能利用 cancel 方法。
您的方法 sendCommandAndWaitForResponse: 成为异步完成处理程序:
- (void)sendCommand:(NSData *)request
completion:(DataToStreamCopier_completion_t)completionHandler
{
DataToStreamCopier* op = [DataToStreamCopier initWithData:request
destinationStream:self.outputStream
completion:completionHandler];
[op start];

// setup timeout with block: ^{ [op cancel]; }
...
}

用法:
[self sendCommand:request completion:^(id result) {
if ([result isKindOfClass[NSError error]]) {
NSLog(@"Error: %@", error);
}
else {
// execute on a certain execution context (main thread) if required:
dispatch_async(dispatch_get_main_queue(), ^{
APDUResponse* response = result;
...
});
}
}];

警告:
不幸的,执行并发 NSOperation 子类正常使用雇用一个运行的循环的基本任务不是那么微不足道作为它应该是。那里将会出现微妙的并发问题,迫使您可以使用同步基元像锁或调度队列和其他几个技巧,使它真正可靠。
幸运的,换任何运行循环任务并发 NSOperation 子类基本上要求相同的"锅炉板"代码。所以,一旦你有一个通用的解决方案,了编码的工作量是只是复制和粘贴从"模板",然后为您的特定目的定制代码。
替代的解决方案:
严格地说,你甚至不需要一个子类,
NSOperation 如果你不打算放入这些任务数 NSOperationQueue 。可以简单地将它发送入门并发操作 start
方法-有没有 NSOperationQueue 所需。然后,不使用类的子类 NSOperation 可以使您自己的实现比较简单,因为子类
NSOperation 本身有其自己微妙之处。
但是,你其实需要换行您运行循环驾驶"操作对象" NSStream 对象,因为执行需要保留状态,不能在一个简单的异步方法完成。
因此,您可以使用任何自定义的类,可以查看作为异步操作有 start 和 cancel 方法和有一种机制来通知调用站点完成基本任务。
也有更强大的手段来通知调用站点比完成处理程序。例如: 承诺或期货 (见 wiki 文章期货和承诺)。
假设作为一种手段,例如通知调用站点,实现您自己的"异步操作"类的承诺:
@interface WriteDataToStreamOperation : AsyncOperation

- (void) start;
- (void) cancel;

@property (nonatomic, readonly) BOOL isCancelled;
@property (nonatomic, readonly) BOOL isExecuting;
@property (nonatomic, readonly) BOOL isFinished;
@property (nonatomic, readonly) Promise* promise;

@end

你原来的问题会更多"同步"-尽管正在异步的窗台:
您 sendCommand 方法将成为:
注:假定承诺类的某些实现:
- (Promise*) sendCommand:(NSData *)command {
WriteDataToStreamOperation* op =
[[WriteDataToStreamOperation alloc] initWithData:command
outputStream:self.outputStream];
[op start];
Promise* promise = op.promise;
[promise setTimeout:100]; // time out after 100 seconds
return promise;
}

注:承诺已设置"超时"。这基本上注册一个计时器和一个处理程序。如果之前承诺的底层任务获取解决激发计时器,计时器块解析超时错误的诺言。如何
(和如果) 这实施取决于承诺图书馆。(在这里,我假设的 RXPromise 库,我在哪里作者。其他执行也可以实现这种功能)。
用法:
[self sendCommand:request].then(^id(APDUResponse* response) {
// do something with the response
...
return ...; // returns the result of the handler
},
^id(NSError*error) {
// A Stream error or a timeout error
NSLog(@"Error: %@", error);
return nil; // returns nothing
});

替代用法:
您可能会以不同的方式设置超时时间。现在,假设我们没有设置超时时间内的 sendCommand: 方法。
我们可以设置"外部"超时:
Promise* promise = [self sendCommand:request];
[promise setTimeout:100];
promise.then(^id(APDUResponse* response) {
// do something with the response
...
return ...; // returns the result of the handler
},
^id(NSError*error) {
// A Stream error or a timeout error
NSLog(@"Error: %@", error);
return nil; // returns nothing
});

使异步方法同步
通常,你不需要和不应该"转换"的异步方法的几种同步方法在应用程序代码中。这总是导致次优和效率低下的代码不必要地消耗系统资源,线程类似。
然而,你可能会想要这样做是有意义的单元测试中:
"同步"在单元测试中的异步方法的示例
测试您的实施时, 经常要"等待"(是同步) 的结果。你基础的任务实际上正在运行的循环,执行这一事实可能在相同的线程要等待结果的位置,不会使解决方案更简单。
但是,您可以轻松完成此与 RXPromise 图书馆利用 runLoopWait 方法该方法有效地进入运行的循环并没有等待解决的承诺:
-(void) testSendingCommandShouldReturnResponseBeforeTimeout10 {
Promise* promise = [self sendCommand:request];
[promise setTimeout:10];
[promise.then(^id(APDUResponse* response) {
// do something with the response
XCTAssertNotNil(response);
return ...; // returns the result of the handler
},
^id(NSError*error) {
// A Stream error or a timeout error
XCTestFail(@"failed with error: %@", error);
return nil; // returns nothing

}) runLoopWait]; // "wait" on the run loop
}

在这里,方法 runLoopWait 将输入一个运行的循环,并等待的承诺得到解决,由一个超时错误或底层任务已解决承诺的时候。承诺将不会阻塞主线程和不轮询运行的循环。它只是将留下运行的循环时承诺已得到解决。其他运行的循环事件将会像往常一样处理。
注:您可以安全地调用 testSendingCommandShouldReturnResponseBeforeTimeout10 从主线程不会阻止它。而这是绝对必要的因为你流委托方法可能太在主线程上执行 !
有其他方法通常在单元测试库,其中提供类似的功能来异步方法或操作,而进入运行的循环的结果"等待"中找到。
不建议使用其他方法以异步方法或操作的最终结果为"等待"。这些通常会派遣到私人的线程的方法,然后阻止它,直到结果可用。
有用资源
操作像类,将流复制到另一个流利用的承诺 (上要点) 的代码段: RXStreamToStreamCopier本回答被网友采纳

SendRequest 有时候会出现超时现象怎么解决
在这里,方法 runLoopWait 将输入一个运行的循环,并等待的承诺得到解决,由一个超时错误或底层任务已解决承诺的时候。承诺将不会阻塞主线程和不轮询运行的循环。它只是将留下运行的循环时承诺已得到解决。其他运行的循环事件将会像往常一样处理。注:您可以安全地调用 testSendingCommandShouldReturnResponseBeforeTimeout10 ...

request timed out 是什么意思,要怎么解决
1. 检查网络连接:确保网络连接稳定可靠,如果网络连接存在问题,可以尝试重新连接网络或切换到其他网络环境。2. 检查服务器状态:如果服务器出现故障或负载过高,可以尝试联系服务器管理员或提供商,了解服务器状态并进行相应的处理。3. 优化请求数据量:如果请求数据量过大,可以考虑优化请求数据,减少不必...

淘宝助理出现错误 怎么解决呀?
解决方法:关掉你的防火墙,或是在防火墙的设置里将淘宝助理3设置为允许访问网络 2)提示错误码:错误信息:DISKERR_CREATE_READING_FILE 你的店铺添加了新的分类,而助理没有及时进行数据更新(有时候需要强制更新)。如果是引用的第三方数据,在没有成功上传前,不能随便移动数据所在的文件夹和磁盘分区。

ios开发中sendsynchronousrequest被废弃以后怎么解决
os开发中两个常见问题解决方法一、“Unknown class XXViewController in Interface Builder file.”问题处理最近在静态库中写了一个XXViewController类,然后在主工程的xib中,将xib的类指定为XXViewController,程序运行时,报了如下错误:“Unknown class XXViewController in Interface Builder file.”之前...

访问php页面出现504 Gateway Timeout 怎么解决
把max_children由之前的10改为现在的30,这样就可以保证 有充足的php-cgi进程可以被使用;把request_terminate_timeout由之前的0s改为60s,这样php-cgi进程 处理脚本的超时时间就是60秒,可以防止进程都被挂起,提高利用效率。接着再更改nginx的几个配置项,减少FastCGI的请求次 数,尽量维持buffers不变...

网页出现“504 Gateway Time-out”,没有其它信息是什么原因?
后端响应缓慢,导致代理服务器接收后端响应超时了。服务器出现504 Gateway Time-out的解决方法!按照上述可能的两个情况,逐一分析。(1)x.php程序在特定的情况下,确实运行缓慢,但apache得access log在25秒左右的时候成功记录了200访问日志(由于php代码执行结束后才记录日志,一开始可能看不到access日志,...

网页出现“504 Gateway Time-out”,没有其它信息是什么原因?
当然如果在进行某一项即时的操作, 可能需要nginx的超时参数调大点, 例如设置成60秒:send_timeout 60;经过这两个参数的调整,一般不会再提示“504 Gateway Time-out”错误,问题基本解决。情况二:PHP环境的配置问题有时候网站需要对php-fpm和nginx进行配置修改。因为这种情况下,也会出现“504 Gateway ...

504 Gateway Time-out 是什么意思啊这 怎么解决啊
情况一解决办法(改进):在上述方法修改后,如果还是出现问题,可以继续修改nginx的超时参数,将参数调大一点,如设置为60秒:send_timeout 60;经过这两个参数的调整,结果没有再提示“504 Gateway Time-out”错误,说明效果还是挺不错的,问题基本解决。情况二:PHP环境的配置问题 这里需要对php-fpm和...

什么是PING指令,有什么用?
如果我们在Tracert命令后面加上一些参数,还可以检测到其他更详细的信息,例如使用参数-d,可以指定程序在跟踪主机的路径信息时,同时也解析目标主机的域名。Netstat Netstat命令可以帮助网络管理员了解网络的整体使用情况。它可以显示当前正在活动的网络连接的详细信息,例如显示网络连接、路由表和网络接口信息,...

串口通信协议有哪些?
第一组线是RTS(Request to Send)和CTS(Clear toSend)。当接收方准备好接收数据,它置高RTS线表示它准备好了,如果发送方也就绪,它置高CTS,表示它即将发送数据。另一组线是DTR(DataTerminal Ready)和DSR(Data SetReady)。这些线主要用于Modem通信。使得串口和Modem通信他们的状态。例如:当Modem已经准备好接收来自PC...

相似回答