2016-11-16 86 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

請提出修改建議,使這個答案更好:)