iOS開發之Network框架開發Socket實踐

WWDC 2018上,Apple推出了一個新的底層網絡框架 Network.framework,Apple 但願在開發 Socket API 時採用這個新的框架,URLSession 底層就是使用它完成基礎鏈接的。它有以下的特色:java

  • 智能創建鏈接
  • 經優化的數據傳輸
  • 內建的安全加密
  • 無縫兼容移動網絡
  • 原生 Swift 支持

Socket開發

Socket開發步驟通常以下:編程

  1. 創建鏈接
  2. 發送數據
  3. 接收數據

其中最關鍵也最複雜的就是創建鏈接,在 Network.framework 中,使用 NWConnection 建立鏈接,它須要提供參數NWEndpoint(IP與Port) 和 NWParametersswift

NWConnection(host: NWEndpoint.Host("192.168.0.175"),
             port: NWEndpoint.Port(integerLiteral: 9999), 
             using: self.params)
複製代碼

有了 NWConnection 對象之後就能夠利用它進行鏈接,而後發送和接收數據了,下面以一個案例來小試牛刀,看看這個框架是否是真的香。安全

Socket Server

這邊採用的是Java編寫的一個簡單的服務器端(本人熟悉Java),用其餘語言也能夠。主要功能就是建立一個ServerSocket,監聽 9999 端口,等待客戶端鏈接,鏈接成功後接收客戶端發來的信息並打印出來,而後向客戶端發送一條數據。代碼以下:服務器

import java.net.*;
import java.io.*;

public class GreetingServer extends Thread {
    private ServerSocket serverSocket;
    public GreetingServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void run() {
        System.out.println("等待遠程鏈接,端口號爲:" + serverSocket.getLocalPort() + "...");
        byte[] bytes = new byte[50];
        while (true) {
            try {
                Socket server = serverSocket.accept();
                System.out.println("遠程主機地址:" + server.getRemoteSocketAddress());
                DataInputStream in = new DataInputStream(server.getInputStream());
                in.read(bytes);
                System.out.println(server.getRemoteSocketAddress() + ": " + new String(bytes).trim());
                DataOutputStream out = new DataOutputStream(server.getOutputStream());
                String resp = server.getLocalSocketAddress() + ": 謝謝鏈接我,Goodbye!";
                out.write(resp.getBytes("utf-8"));
                System.out.println("消息:'" + resp + "' 已發送");
                server.close();
            } catch (SocketTimeoutException s) {
                System.out.println("Socket timed out!");
                break;
            } catch (IOException e) {
                e.printStackTrace();
                break;
            }
        }
    }

    public static void main(String[] args) {

        try {
            Thread t = new GreetingServer(9999);
            t.run();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
複製代碼

Socket Client

  1. 界面:三個按鈕,分別綁定 建立鏈接發送數據接收數據三個事件
  2. 設置 NWParameters,爲建立的鏈接設置參數(能夠不設置,用系統自帶便可)
  3. 建立 NWConnection對象,而後發起鏈接,監聽鏈接狀態,等待鏈接進入 ready 狀態,只有進入這個狀態表明鏈接成功,能夠進行數據交互了
  4. 利用 NWConnection對象的 send方法發送數據
  5. 利用 NWConnection對象的 receiveMessage 方法接收數據
import UIKit
import Network

class ViewController: UIViewController {
    
    //鏈接參數
    var params: NWParameters!   
    //鏈接對象
    var connection: NWConnection!
    //隊列
    let myQueue = DispatchQueue.global()
    
    override func viewDidLoad() {
        
        super.viewDidLoad()     
        self.setParams()
    }

    //1. 開始創建鏈接
    @IBAction func connect(_ sender: Any) {        
        self.connect()     
    }
    
    //2. 發送數據
    @IBAction func send(_ sender: Any) {   
        sendData()     
    }
    
    //3. 接收數據
    @IBAction func receive(_ sender: Any) { 
        receiveData()  
    }
}


extension ViewController {
    
    //可自定義設置鏈接參數
    private func setParams(){
        
        //使用 TCP 協議
        self.params = NWParameters.tcp       
        //僅使用蜂窩網絡、 Wifi
        params.prohibitedInterfaceTypes = [.wifi, .cellular]
        //使用 IPv6 協議
        if let ipOption = params.defaultProtocolStack.internetProtocol as? NWProtocolIP.Options {    
            ipOption.version = .v6
        }
        //禁止代理
        params.preferNoProxies = true 
    }

    //鏈接
    private func connect() {
        //建立鏈接對象
        self.connection = NWConnection(host: NWEndpoint.Host("192.168.0.175"),
                                       port: NWEndpoint.Port(integerLiteral: 9999), using: self.params)        
        //開始鏈接
        self.connection.start(queue: self.myQueue)   
        //監聽鏈接狀態
        self.connection.stateUpdateHandler = {         
            (newState) in
            switch newState {      
            case .ready:                
                print("state ready")
            case .cancelled:                
                print("state cancel")                
            case .waiting(let error):                
                print("state waiting \(error)")                
            case .failed(let error):                
                print("state failed \(error)")                
            default:
                break
            }
            
        }
    }
    
    //發送數據
    private func sendData(){
        
        let content = "你好,我是iOS客戶端"        
        self.connection?.send(content: content.data(using: .utf8), completion: .contentProcessed({ (sendError) in        
            if let sendError = sendError {                
                print(sendError)                
            } else {                
                print("消息已發送,內容爲: \(content)")
            }
            
        }))
    }
    
    
    //接收數據
    private func receiveData(){ 
          
        self.connection.receiveMessage(completion: { (content, context, isComplete, receError) in            
            if let receError = receError {            
                print(receError)               
            } else {               
                let data = String(data: content ?? "empty".data(using: .utf8)!, encoding: .utf8)                
                print(data!)
            }
            
            if isComplete {                
                //關閉資源
                //self.connection.cancel()
                
            }
        })
    }  
}
複製代碼

測試

  • 運行服務器端的Java程序網絡

  • 運行iOS客戶端框架

  • 依次點擊客戶端的 建立鏈接發送數據 按鈕,服務器輸出 tcp

    服務器.png

  • 點擊客戶端的 接收數據 ,客戶端輸出 ide

    客戶端.png

參考文獻

WWDC 2018:Network.framework 入門,現代化 Socket 編程的新選擇post

相關文章
相關標籤/搜索