【问题标题】:NSStream TCP Keep-alive iOSNSStream TCP Keep-alive iOS
【发布时间】:2013-03-25 00:49:45
【问题描述】:

我已经编写了这段代码来设置与服务器的流:

-(void)streamOpenWithIp:(NSString *)ip withPortNumber:(int)portNumber;
{
       CFReadStreamRef readStream;
       CFWriteStreamRef writeStream;
       CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)ip, portNumber, &readStream, &writeStream);

       if(readStream && writeStream)
       {
            CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);

            //Setup inpustream
            inputStream = (__bridge NSInputStream *)readStream;
            [inputStream setDelegate:self];
            [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [inputStream open];

            //Setup outputstream
            outputStream = (__bridge NSOutputStream *)writeStream;
            [outputStream setDelegate:self];
            [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [outputStream open];
       }
}

我能够连接到服务器并发送和接收数据。但我想检查连接是否仍然存在。如果我从 wifi 路由器上断开电缆,我仍然可以在流中发送数据并且没有发生错误。

您可以在应用程序级别解决此问题,方法是使用计时器发送一些消息并检查您是否收到回复。但是您也可以在较低级别使用 TCP Keep-Alive 技术解决此问题。

我如何用 NSStream 实现这个?如何设置检查间隔?

我假设您在流关闭时通过 TCP Keep-Alive 检查得到 NSStreamEventErrorOcurred?

我已经检查了这篇文章,但我无法弄清楚: Keeping a socket connection alive in iOS

感谢您的帮助!

【问题讨论】:

    标签: ios objective-c tcp keep-alive nsstream


    【解决方案1】:

    类似问题有更完整的答案。

    对于在 10 秒后开始发送 keepalive、每 2 秒发送一次keppalive 并在 4 次 keepalive 后无回复关闭流的应用示例,请查看此帖子: Is it possible to activate TCP keepalive on Apple iOS devices

    它还显示了如何设置重新传输超时,然后关闭连接。

    【讨论】:

      【解决方案2】:

      您可以使用

      获取本机套接字句柄
      CFDataRef socketData = CFReadStreamCopyProperty((__bridge CFReadStreamRef)(inputStream), kCFStreamPropertySocketNativeHandle);
      CFSocketNativeHandle socket;
      CFDataGetBytes(socketData, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8 *)&socket);
      CFRelease(socketData);
      

      然后设置套接字选项(为此您需要#include <sys/socket.h>):

      int on = 1;
      if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) {
         NSLog(@"setsockopt failed: %s", strerror(errno));
      }
      

      您可以将此代码放在kCFStreamEventOpenCompleted 事件的事件处理函数中:

      - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event {
          switch (event) {
              case kCFStreamEventOpenCompleted:
                  if (stream == self.inputStream) {
                      CFDataRef socketData = CFReadStreamCopyProperty((__bridge CFReadStreamRef)(stream), kCFStreamPropertySocketNativeHandle);
                      CFSocketNativeHandle socket;
                      CFDataGetBytes(socketData, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8 *)&socket);
                      CFRelease(socketData);
      
                      int on = 1;
                      if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) {
                          NSLog(@"setsockopt failed: %s", strerror(errno));
                      }
                  }
                  break;
      
              /* ... other cases ... */;
          }
      }
      

      【讨论】:

      • 你能解释一下代码中发生了什么吗?我不明白。我不必再将 NSStream 与委托方法一起使用?
      • @JimCraane:您仍然使用委托方法。我的答案的第一部分显示了如何从 NSInputStream 获取本机 UNIX 套接字描述符。第二部分展示了如何设置 SO_KEEPALIVE 选项。我添加了第三部分,展示了我认为您可以如何将其集成到您的代码中。 - 设置 SO_KEEPALIVE 后,如果服务器断开连接,您应该会收到 kCFStreamEventErrorOccurred 事件。
      • NSStreamEventOpenCompleted 和 kCFStreamEventOpenCompleted 的值都是 1,所以重复大小写。所以我把代码放在 NSStreamEventOpenCompleted 中。之后我运行程序并建立连接。比从路由器上断开 UTP 电缆。现在我想在 30 秒之内出现错误。 TCP Keep-Alive 的标准时间间隔是多少?我可以改变它吗?很抱歉所有这些问题。
      • @JimCraane:NSStreamEventOpenCompleted 很好(我从其他来源复制/粘贴错误)。 - 默认超时取决于系统,我不能告诉你太多。在 Mac OS X 上,它似乎是 75 秒(“sudo sysctl net.inet.tcp.keepintvl”)。从理论上讲,您可以使用“sysctl”在系统范围内配置它,或者使用“setsockopt”在每个套接字的基础上进行配置,但我从未这样做过。
      • 非常感谢您的帮助!
      猜你喜欢
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 2017-02-19
      • 1970-01-01
      • 1970-01-01
      • 2014-07-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多