我有一个下载任务,首先调用REST API,服务器需要生成一个相当大的文件,这需要几分钟来生成,因为它是CPU和磁盘IO密集型的。客户机等待服务器使用它生成的文件的URL给出JSON响应。然后,在获得第一个结果后开始文件下载。
对于生成特别大的文件的调用,这会导致服务器响应非常慢,我看到的是我的代码没有启动的重复请求。
最初,在服务器端工作的人告诉我重复请求的事情。然后,我设置了一种检查网络流量的方法。这是通过设置连接到有线网络的Mac并启用网络共享和使用Proxyman来检查从iPhone到API服务器的流量来完成的。我在网络层看到同一个API请求的多个实例,但我的代码从未被通知过。
代码如下所示
@objc class OfflineMapDownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
@objc func download(){
let config = URLSessionConfiguration.background(withIdentifier: "OfflineMapDownloadSession")
config.timeoutIntervalForRequest = 500
config.shouldUseExtendedBackgroundIdleMode = true
config.sessionSendsLaunchEvents = true
urlSession = URLSession(configuration: config, delegate: self, delegateQueue: nil)
getMapUrlsFromServer(bounds)
}
func getMapUrlsFromServer(){
var urlString = "http://www.fake.com/DoMakeMap.php"
if let url = URL(string: urlString) {
let request = NSMutableURLRequest(url: url)
//...Real code sets up a JSON body in to params...
request.httpBody = params.data(using: .utf8 )
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.timeoutInterval = 500
urlSession?.configuration.timeoutIntervalForRequest = 500
urlSession?.configuration.timeoutIntervalForResource = 500
request.httpShouldUsePipelining = true
let backgroundTask = urlSession?.downloadTask(with: request as URLRequest)
backgroundTask?.countOfBytesClientExpectsToSend = Int64(params.lengthOfBytes(using: .utf8))
backgroundTask?.countOfBytesClientExpectsToReceive = 1000
backgroundTask?.taskDescription = "Map Url Download"
backgroundTask?.resume()
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
if (downloadTask.taskDescription == "CTM1 Url Download") {
do {
let data = try Data(contentsOf: location, options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
if let jsonResult = jsonResult as? Dictionary<String, AnyObject> {
if let ctm1Url = jsonResult["CTM1Url"] as? String {
if let filesize = jsonResult["filesize"] as? Int {
currentDownload?.ctm1Url = URL(string: ctm1Url)
currentDownload?.ctm1FileSize = Int32(filesize)
if (Int32(filesize) == 0) {
postDownloadFailed()
} else {
startCtm1FileDownload(ctm1Url,filesize)
}
}
}
}
} catch {
postDownloadFailed()
}
}
} 这个download类还有更多功能,因为一旦第一个api调用完成,它就会下载实际的文件。由于问题发生在代码执行之前,因此我没有将其包含在示例代码中。
Proxyman的日志显示API调用在(分钟:秒) 46:06、47:13、48:21、49:30、50:44、52:06、53:45发出

看起来该请求的重复时间间隔仅超过1分钟。
有一个API字段,我可以在其中放置任何值,它将由服务器回显给我。我在那里放了一个用CACurrentMediaTime()生成的时间戳,然后登录Proxyman,结果显示确实是同一个API调用,所以我的代码不可能被多次调用。看起来好像iOS网络层正在重新发出http请求,因为服务器需要很长时间才能响应。这最终会在服务器上造成问题,API也会失败。
任何帮助都将不胜感激。
发布于 2021-06-24 03:52:24
我认为问题出在使用URLSessionConfiguration.background(withIdentifier:)进行此api调用时。
使用此方法初始化一个配置对象,该对象适合在应用程序在后台运行时传输数据文件。使用此对象配置的会话将传输控制权移交给系统,系统在单独的进程中处理传输。在iOS中,此配置使传输即使在应用本身被挂起或终止时也可以继续。
因此,问题在于,由于这种错误的API使用,系统正在不必要地重试您的请求。
以下是我的建议-
发布于 2021-06-28 01:16:06
这听起来很像TCP重传。如果客户端发送TCP数据段,而服务器在短时间内未确认收到,则客户端假定数据段未到达目的地,然后再次发送数据段。这是一种比URLSession低得多的机制。
这个API使用的HTTP服务器应用程序也有可能(比如Apache、IIS、LigHTTPd、nginx等)。被配置为使用响应数据进行确认,以节省打包和成帧开销。如果是这样,并且如果响应数据花费的时间超过客户端的TCP重新传输超时,您将得到此行为。
您是否有连接的数据包捕获?如果没有,请尝试使用tcpdump收集一个并在Wireshark中查看。如果我是对的,您将看到多个请求,并且它们都具有相同的序列号。
至于如何解决它,如果这是问题,我不确定。服务器应在收到请求后立即进行确认。
https://stackoverflow.com/questions/67928903
复制相似问题