SocketException:主机查找失败:'...com'(操作系统错误:提供了节点名或服务名,或未知,errno = 8)

Tap*_*Pal 5 socketexception retrofit dart-http flutter dio

我们处于生产应用程序面临以下套接字异常并且在此之后无法执行任何其他网络操作的情况。 

DioError [DioErrorType.DEFAULT]: SocketException: Failed host lookup: ‘xyz.abc.com’ (OS Error: nodename nor servname provided, or not known, errno = 8)
Run Code Online (Sandbox Code Playgroud)

注意:反复遇到一位使用 iPhone X、iOS 14.4 的用户

我们使用Dio作为网络客户端,使用Retrofit,它在内部使用来自 dart 的 HttpClient。使用Dio,模拟环境无法重现异常,但直接使用HttpClient,可以在iOS模拟器中使用以下代码重现相同的异常。

HttpClient userAgent = new HttpClient();
  bool run = true;
  while (run) {
    try {
      await userAgent.getUrl(Uri.parse('https://www.google.com'));
      print('Number of api executed');
    } catch (e) {
      print(e);
      if (e is SocketException) {
        if ((e as SocketException).osError.errorCode == 8)
          print('***** Exception Caught *****');
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

一旦抛出异常,HttpClient 就无法从该陈旧状态中恢复,并且所有其他 API 请求都开始失败并出现相同的错误。

在此处输入图片说明

通过强制关闭所有以前的连接并打开一个新的 HttpClient,我们能够从那个陈旧的状态中恢复。

  HttpClient userAgent = new HttpClient();
  bool run = true;
  while (run) {
    try {
      await userAgent.getUrl(Uri.parse('https://www.google.com'));
      print('Number of api executed');
    } catch (e) {
      print(e);

      if (e is SocketException) {
        if ((e as SocketException).osError.errorCode == 8)
          print('***** Exception Caught *****');
      }
      userAgent.close(force: true);
      print('Force closing previous connections');
      userAgent = HttpClient();
      print('Creating new HttpClient instance');
    }
  }
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

一个有趣的事实是在每 236 次请求之后就会引发异常。这可能是因为文件描述符过度使用,但 iOS 的限制为 256。

有了稳定的互联网连接,这个问题每次都可以在 iOS 模拟器中重现。

虽然我无法用 Dio 客户端重现该问题,但在生产中它正在发生。所以我正在寻求帮助以了解这个问题的根本原因,以及我们如何预防它?

任何遇到这种情况的人以及您是如何克服它的,请帮助我。

提前致谢。

Bak*_*ker 1

这是一个奇怪的错误。

\n

这可能无法回答您的问题,但可能会促使我们弄清楚发生了什么。

\n

stream代码片段(从问题复制)将在每次调用时打开一个新的.getUrl(),并且不会关闭它们。(我假设这是故意创建套接字异常?)

\n
HttpClient userAgent = new HttpClient();\n  bool run = true;\n  while (run) {\n    try {\n      await userAgent.getUrl(Uri.parse('https://www.google.com'));\n      print('Number of api executed');\n    } catch (e) {\n      print(e);\n      if (e is SocketException) {\n        if ((e as SocketException).osError.errorCode == 8)\n          print('***** Exception Caught *****');\n      }\n    }\n  }\n
Run Code Online (Sandbox Code Playgroud)\n

在某些时候,(开放流的)会达到限制。我猜你的情况下这个神奇数字是 236。

\n

那么在那时,您是在看到nodename or servname provided异常的时候吗?

\n

(顺便说一句,我认为错误来自底层主机操作系统的 DNS 服务,尽管我不确定是否是由于请求垃圾邮件、打开连接的数量等造成的。这可能不是相关信息.)

\n

因此,如果您以典型的方式使用HttpClient,发出请求并关闭那些打开的流,如下所示:

\n
      var request = await userAgent.getUrl(Uri.parse('http://example.com/'));\n      var response = await request.close(); // \xe2\x86\x90 close the stream\n      var body = await response.transform(utf8.decoder).join();\n      // \xe2\x86\x91 convert results to text\n      // rinse, repeat... \n
Run Code Online (Sandbox Code Playgroud)\n

...您是否仍然看到nodename or servname provided弹出相同的错误?

\n

使用上面的“典型用法”代码,userAgent可以重复使用,直到userAgent.close()进行调用(并且 HttpClient 永久关闭。\n尝试再次使用它会引发异常Bad State)。

\n

我很想知道修改后的代码是否仍然会出现节点名错误。

\n
\n

回复:问题的第二个代码片段。

\n

在 catch 块中,HttpClient关闭,然后HttpClient创建一个新的。这有效地关闭了块中打开的所有打开流try(我假设重置打开流的限制。)

\n

如果您调整第二个代码示例以使用:

\n
      var req = await userAgent.getUrl(Uri.parse('https://www.google.com'));\n      userAgent.close(force: true);\n      userAgent = HttpClient();\n      print('Number of api executed');\n
Run Code Online (Sandbox Code Playgroud)\n

你能无限期地运行吗?

\n