iOS 16中URLSessionUploadTask上传陷入无限循环问题排查
问题分析与解决方案
核心循环诱因
- 认证挑战时序冲突+请求体重复发送:服务器先触发SSL信任挑战,客户端处理后发送请求体,随后服务器返回HTTP Basic认证要求——URLSession会重新发起完整请求流程,包括重发请求体。你的请求体是字符串构造的multipart数据,可能在重发时出现字节数翻倍(比如字符串拼接逻辑重复),再加上认证凭证未被正确缓存,导致循环触发。
- 请求体格式无效:用字符串而非真实二进制zip数据构造multipart请求,服务器无法解析请求,返回异常响应(比如误判认证状态),触发客户端重试逻辑。
分步修复方案
1. 修复HTTP Basic认证凭证缓存逻辑
使用带代理的URLSession实例,确保处理认证挑战时正确缓存凭证,避免重复触发认证:
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { // 处理HTTP Basic认证 if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic { let credential = URLCredential(user: "你的用户名", password: "你的密码", persistence: .permanent) return (.useCredential, credential) } // 处理服务器信任挑战(针对自签名证书场景) if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { guard let serverTrust = challenge.protectionSpace.serverTrust else { return (.performDefaultHandling, nil) } let credential = URLCredential(trust: serverTrust) return (.useCredential, credential) } return (.performDefaultHandling, nil) }
注意:不要使用
URLSession.shared,需手动初始化带代理的URLSession实例,才能正确复用缓存的认证凭证。
2. 重构请求体为二进制格式
替换字符串构造multipart请求的逻辑,直接用Data拼接真实二进制zip数据,避免编码或重复拼接导致的字节异常:
func buildValidMultipartData(boundary: String, zipData: Data, fileName: String) -> Data { let formData = NSMutableData() // 添加表单边界与字段描述 formData.append("--\(boundary)\r\n".data(using: .utf8)!) formData.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!) formData.append("Content-Type: application/zip\r\n\r\n".data(using: .utf8)!) // 追加真实二进制zip数据 formData.append(zipData) // 闭合表单边界 formData.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) return formData as Data }
3. 禁用URLSession自动重试(可选)
若服务器端认证时序存在问题,可通过配置禁用自动重试,避免循环触发:
let sessionConfig = URLSessionConfiguration.default sessionConfig.httpMaximumConnectionsPerHost = 1 sessionConfig.timeoutIntervalForRequest = 30 // 禁用自动重试逻辑 sessionConfig.shouldUseExtendedBackgroundIdleMode = false let customSession = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
额外排查建议
- 抓包验证请求:用Charles或Wireshark抓取请求,确认每次循环的请求头、请求体是否重复,服务器返回的状态码(比如是否重复返回401未授权)。
- 检查服务器日志:确认服务器是否收到请求体,是否因格式错误返回异常状态码触发客户端重试。
内容的提问来源于stack exchange,提问作者Fritz Anderson
相关产品推荐
相关产品推荐

