【问题标题】:Keep Alive not working properly on iOSKeep Alive 在 iOS 上无法正常工作
【发布时间】:2017-05-19 19:52:42
【问题描述】:

我目前正在开发一个应用程序,我们需要一些请求来尽快访问我们的服务器。为了加快请求过程,我们必须消除握手(因为它需要额外的时间)并建立永久连接。

应用程序使用 Alamofire 框架向我们的服务器发出所有请求,设置如下:

我们有一个使用默认配置和 http 标头设置的会话管理器。

lazy var sessionManager: Alamofire.SessionManager = {
    let configuration = URLSessionConfiguration.default
    configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
    let manager = Alamofire.SessionManager(configuration: configuration)
    return manager
}()

会话管理器在所有请求中都是持久的。每个请求都使用以下代码进行:

self.sessionManager.request(request.urlString, method: request.method, parameters: request.parameters)
            .responseJSON { [weak self] response in
    // Handle the response
}

request.urlString 是我们服务器“http://ourserver.com/example”的url

request.method 设置为 post

request.parameters 是一个参数字典

请求工作正常,我们得到了有效的响应。 问题出现在保活计时器上,我们的服务器将其设置为 300 秒。设备在 wifi 上最多保持连接 30 秒,并通过 GSM 几乎立即关闭。


服务器调试

我们在服务器上做了一些调试,发现如下结果

测试:

测试一:

  • iPhone 通过 WiFi 连接到互联网

测试 2:

  • iPhone 通过 3G 连接到互联网

行为:

  • 两种情况:应用程序使用“Connection: keep-alive”向 Web 服务器发出 HTTP/1.1 请求;服务器(服务器 ip = 10.217.81.131)以“Keep-Alive: timeout=300, max=99”响应
  • 客户端(测试 1 - WiFi 上的应用程序)在第 30 秒发送 TCP FIN 并关闭连接
  • 客户端(测试 2 – 3G 上的应用程序)在收到来自其第一个 HTTP POST 的 HTTP/1.1 OK 消息后立即(零秒)发送 TCP FIN 请求

测试1在服务器端的日志:

  1. 在 23.101902,应用程序使用“连接:保持活动”向服务器发出 HTTP/1.1 POST 请求

  2. 在 23.139422,服务器响应 HTTP/1.1 200 OK 并使用“Connection: Keep-Alive”和“timeout=300”(300 秒)

  3. 往返时间 (RTT) 报告为 333.82 毫秒(这突出了我们在以下时间戳上的误差幅度):

  1. 然而,该应用程序会在 30 秒内关闭连接(大约考虑到 Internet 传输变化 - 54.200863 和 23.451979 时间戳之间的差异):

  2. 测试重复多次,每次大约始终监控 30 秒的时间

在服务器端测试 2 日志:

  1. 来自应用程序的 HTTP/1.1 POST 请求:
  2. 接受并设置为 300 秒的保持活动状态的 HTTP OK 服务器响应:
  3. RTT 为 859.849 毫秒

应用程序立即关闭连接,其中立即是 21.197918 – 18.747780 = 2.450138 秒

在从 WiFi 切换到 3G 并返回时重复测试,并记录相同的结果。

客户端调试

使用 WiFi

第一次尝试(建立连接)

Optional(
[AnyHashable("Content-Type"): text/html,

AnyHashable("Content-Encoding"): gzip, 

AnyHashable("Content-Length"): 36, 

AnyHashable("Set-Cookie"): user_cookieuser_session=HXQuslXgivCRKd%2BJ6bkg5D%2B0pWhCAWkUPedUEGyZQ8%2Fl65UeFcsgebkF4tqZQYzVgp2gWgAQ3DwJA5dbXUCz4%2FnxIhUTVlTShIsUMeeK6Ej8YMlB11DAewHmkp%2Bd3Nr7hJFFQlld%2BD8Q2M46OMRGJ7joOzmvH3tXgQtRqR9gS2K1IpsdGupJ3DZ1AWBP5HwS41yqZraYsBtRrFnpGgK0CH9JrnsHhRmYpD40NmlZQ6DWtDt%2B8p6eg9jF0xE6k0Es4Q%2FNiAx9S9PkhII7CKPuBYfFi1Ijd7ILaCH5TXV3vipz0TmlADktC1OARPTYSwygN2r6bEsX15Un5WUhc2caCeuXnmd6xy8sbjVUDn72KELWzdmDTl6p5fRapHzFEfGEEg2LOEuwybmf2Nt6DHB6o6EA5vfJovh2obpp4HkIeAQ%3D; expires=Sun, 08-Jan-2017 12:51:43 GMT; path=/,

AnyHashable("Keep-Alive"): timeout=300, max=100, 

AnyHashable("Connection"): Keep-Alive, 

AnyHashable("X-Powered-By"): PHP/5.3.10-1ubuntu3.11, 

AnyHashable("Server"): Apache/2.2.22 (Ubuntu), 

AnyHashable("Vary"): Accept-Encoding, 

AnyHashable("Date"): Sun, 08 Jan 2017 10:51:43 GMT])

第二次尝试(30 秒内,连接仍然有效)

Optional([AnyHashable("Content-Type"): text/html, 

AnyHashable("Content-Encoding"): gzip, 

AnyHashable("Content-Length"): 36, 

AnyHashable("Keep-Alive"): timeout=300, max=99, 

AnyHashable("Connection"): Keep-Alive, 

AnyHashable("X-Powered-By"): PHP/5.3.10-1ubuntu3.11, 

AnyHashable("Server"): Apache/2.2.22 (Ubuntu), 

AnyHashable("Vary"): Accept-Encoding, 

AnyHashable("Date"): Sun, 08 Jan 2017 11:00:18 GMT])

然后在 30 秒后连接断开 (FI)

使用 3G

第一次尝试

Optional([AnyHashable("Content-Type"): text/html, 

AnyHashable("Content-Encoding"): gzip, 

AnyHashable("Content-Length"): 36, 

AnyHashable("Connection"): keep-alive, 

AnyHashable("X-Powered-By"): PHP/5.3.10-1ubuntu3.11, 

AnyHashable("Server"): Apache/2.2.22 (Ubuntu), 

AnyHashable("Vary"): Accept-Encoding, 

AnyHashable("Date"): Sun, 08 Jan 2017 11:04:31 GMT])

然后连接几乎立即断开。

【问题讨论】:

  • 您是否还可以记录从服务器发送到您的应用程序的标头?请注意,某些代理可能会更改 keep-alive 标头。
  • 您是否尝试过在客户端请求中使用“Keep-Alive”而不是“keep-alive”?规范有点模棱两可,但可能很重要。
  • @Sulthan 我在问题末尾的客户端调试下添加了从服务器进入应用程序的标头。我可以看到在 wifi 测试中保持活动有超时和最大值,而在 3g 测试中则没有。我想知道这是否是正常行为。
  • @zirinisp 这可能是您的运营商造成的。在 3G 上保持连接并不容易,因为您的 IP 可能经常变化。
  • @Sulthan 我们监控了 ip 更改,但我们没有。然而,连接正在断开。

标签: ios swift xcode alamofire nsurlsession


【解决方案1】:

现在我再次查看了代码,我想我发现了问题所在。底层的NSURLSession 类默认忽略keep-alive 标头,因为一些服务器“支持”它,但实际上,如果你真的尝试使用它,IIRC 会严重破坏。

如果您希望会话支持保持活动状态,则必须在会话配置中将HTTPShouldUsePipelining 显式设置为YES

请注意,仍然不能保证连接会保持正常,这取决于 iOS 决定对无线电进行电源管理的积极程度,但至少你会祈祷。 :-)

【讨论】:

  • 我们尝试将 HTTPShouldUsePipelining 设置为 true,得到了相同的结果。
  • 看起来httpShouldUsePipelining 不适用于 POST 请求。正如docs 所说,“如果配置了 HTTP 代理,或者 HTTP 请求使用了不安全的请求方法,例如,POST 请求将不会通过管道传输,这可能无效。”。
  • 我猜有点是有道理的,因为如果请求会影响并发 GET 请求的结果,那么与其他请求并行发送 POST 请求可能会导致不确定性。
猜你喜欢
  • 2015-09-27
  • 2013-03-25
  • 1970-01-01
  • 2022-01-24
  • 2016-09-07
  • 1970-01-01
  • 2021-08-17
  • 2015-05-30
  • 2016-05-11
相关资源
最近更新 更多