2016-11-16 53 views
3

我对Swift编程相当新颖,请原谅我在下面提出的虚拟问题。Swift的调度套接字通信

在我的应用程序中,我试图调度一个函数,它将从我的服务器接收一些数据,并且会每秒调用一次。通信需要通过TCP套接字来实现。在做了一些研究之后,我认为我需要有一种方法来正确使用线程来调用该函数。所以这里来的是我的问题:

  1. 我应该在哪里连接到服务器? (我应该在我的第一个视图控制器的viewDidLoad()函数中建立连接吗?)
  2. 我应该在哪里创建一个线程来安排函数调用?如果我在我的第一个视图控制器中创建线程,在切换到另一个视图控制器之后线程会死掉吗?
  3. 我应该为该线程使用什么QoS等级?该应用每秒钟都会呈现从服务器接收到的数据,因此我认为该任务将具有非常高的优先级。

我试过寻找有关线程和套接字通信的教程和示例,但我找不到适用于我的应用程序的信息。因此,任何有关设计的帮助或见解将不胜感激!

在此先感谢!

+0

您是否计划每次都建立一个新的连接或保持连接?您是否从NSURLSession中探索了内置的API,以查看它们是否满足您的需求? – jtbandes

+0

@jtbandes感谢您的及时回复。我需要为应用程序保留一个持续的TCP连接。我也研究过NSURLSession,但是看起来这个API更适合HTTP RESTful支持(我非常乐意被纠正)。对我而言,不断地向服务器发起新的REST调用会给服务器带来巨大的负担。 – cafemike

回答

0

一些挖后,我发现来实现对雨燕侧流编程(使用本地斯威夫特的能力)的方式。事实证明,Swift中的方式非常类似于Objective C中的方式。

在Swift中启用流编程所需的两个函数是func connect(),我必须自己编写这两个函数,而func stream(_ aStream: Stream, handle eventCode: Stream.Event)一种方法在StreamDelegate

这里是func connect()样子:

func connect() { 
    Stream.getStreamsToHost(withName: <serverIP>, port: <serverPort>, inputStream: &inputStream, outputStream: &) 

    guard let inputStream = inputStream, let outputStream = outputStream else { 
     print(" ->\tNetworkControllerError: Cannot open inputstream/outputstream.") 
     return 
    } 

    // Set delegate 
    inputStream.delegate = self 
    outputStream.delegate = self 

    let socketWorkQueue = DispatchQueue(label: "socketWorkQueue", attributes: .concurrent) 
    CFReadStreamSetDispatchQueue(inputStream, socketWorkQueue) 
    CFWriteStreamSetDispatchQueue(outputStream, socketWorkQueue) 

    inputStream.open() 
    outputStream.open() 
} 

这里是什么func stream(_ aStream: Stream, handle eventCode: Stream.Event)样子:

func stream(_ aStream: Stream, handle eventCode: Stream.Event) { 

    if aStream === inputStream { 
     switch eventCode { 
     case Stream.Event.errorOccurred: 
      streamEventQueue.async { 
       // do something here 
      } 
      break 
     case Stream.Event.openCompleted: 
      streamEventQueue.async { 
       // do something here 
      } 
      break 
     case Stream.Event.hasBytesAvailable: 
      streamEventQueue.async { 
       // do something here 
       let output = read() 
      } 
      break 
     case Stream.Event.endEncountered: 
      streamEventQueue.async { 
       // do something here 
      } 
      break 
     default: 
      break 
     } 
    } 
    else if aStream === outputStream { 
     switch eventCode { 
     case Stream.Event.errorOccurred: 
      streamEventQueue.async { 
       // do something here 
      } 
      break 
     case Stream.Event.openCompleted: 
      streamEventQueue.async { 
       // do something here 
      } 
      break 
     default: 
      break 
     } 
    } 
} 

所以stream()功能应该是在我的应用程序来进行状态转换的唯一的地方由网络连接/断开引起。请注意,我还使用串行事件队列来处理流事件。这是为了防止可能导致国家腐败的任何潜在的竞争条件。

对于那些对流编程不太熟悉的人来说,这是一个服务器 - 客户端通信模式,它保持连接/套接字活着。连接需要保持活跃,主要是由于服务器和客户端之间存在持续通信。像RESTful API这样的通信模式会严重影响服务器的客户请求,因此不推荐使用。

1

据我来说,下面可用于如果你要,只要你启动应用程序使连接到服务器的问题

  1. 介绍的方案进行。我相信这是好做的是,在didFinishLaunchingWithOptionsAppDelegate下面

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 
    
    } 
    
  2. 这要看给你将如何切换到其他ViewController。在切换过程中,如果您要释放当前的ViewController,那么您的线程将会死亡。

  3. 考虑到您正在制作基于套接字的应用程序,并且您将每秒从服务器接收数据。然后,NSURLSession可能对您没有太大帮助。对于套接字通信,通常会使用NSInputStreamNSOutputStream。从客观-C下面的例子可以帮助你开始:

    - (void)connectWithHostFromUrl: (NSURL *)hostUrl { 
    
         CFReadStreamRef readStream; 
         CFWriteStreamRef writeStream; 
         CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)[hostUrl host], 80, &readStream, &writeStream); 
    
         _inputStream = (__bridge_transfer NSInputStream *)readStream; 
         [_inputStream setDelegate:self]; 
         [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
         [_inputStream open]; 
    
         _outputStream = (__bridge_transfer NSOutputStream *)writeStream; 
         [_outputStream setDelegate:self]; 
         [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
         [_outputStream open]; 
        } 
    
    // Delegate methods 
    
    - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { 
    
         switch (eventCode) { 
          case NSStreamEventNone: 
          { 
           // handle it according to your need 
          } 
            break; 
          case NSStreamEventOpenCompleted: 
          { 
           // handle it according to your need 
          } 
            break; 
          case NSStreamEventHasBytesAvailable: 
          { 
           if (_receivedData == nil) { 
             _receivedData = [NSMutableData new]; 
           } 
           uint8_t buffer[1024]; 
           NSInputStream *inputStream = (NSInputStream *)aStream; 
           NSInteger bytesReceived = [inputStream read:buffer maxLength:1024]; 
           if (bytesReceived > 0) { 
            [_receivedData appendBytes:(const void *)buffer length:bytesReceived]; 
           } 
          } 
            break; 
          case NSStreamEventHasSpaceAvailable: 
          { 
            if (_dataToSend != nil) { 
            // _dataToSend is NSMutableData/NSData object 
             NSOutputStream *outputStream = (NSOutputStream *)aStream; 
             const uint8_t *mutableBytes = (const uint8_t *)[_dataToSend mutableBytes]; 
             NSInteger length = [_dataToSend length]/sizeof(uint8_t); 
             [outputStream write:(const uint8_t *)mutableBytes maxLength:length]; 
            } 
           } 
            break; 
           case NSStreamEventErrorOccurred: 
           { 
           // handle it according to your need 
           } 
            break; 
           case NSStreamEventEndEncountered: 
           { 
           // handle it according to your need 
           } 
            break; 
           default: 
            break; 
         } 
    } 
    

因为有很多情况下,在这里处理。大多数情况下,我建议使用经过测试的第三方库,如SocketRocket

请提出修改建议,使这个答案更好:)