iOS swift5 整合 CocoaAsyncSocket三方库 实现socket tcp通讯

由于第一次接触 iOS开发,走了相当多的弯路。

 

最开始使用的是 CocoaAsyncSocket 三方库,刚开始有好多网上写好的代码,作为一个新人,就面向百度编程呗,这一面相可出事了,折腾了两天都没折腾明白!

最开始参考的swift 版本 : https://www.jianshu.com/p/9d629bae702f

扒下来,放到我的代码中。控制台如下:

2021-07-19 11:27:05.373514+0800 NB_Music[5478:1899448] [connection] nw_connection_copy_connected_path [C1] Client called nw_connection_copy_connected_path on unconnected nw_connection
2021-07-19 11:27:05.373790+0800 NB_Music[5478:1899448] [] tcp_connection_is_cellular No connected path
2021-07-19 11:27:05.581082+0800 NB_Music[5478:1899445] [connection] nw_endpoint_handler_set_adaptive_read_handler [C1 192.168.1.144:557 ready socket-flow (satisfied (Path is satisfied), viable, interface: en0, scoped, ipv4, dns)] unregister notification for read_timeout failed
2021-07-19 11:27:05.581260+0800 NB_Music[5478:1899445] [connection] nw_endpoint_handler_set_adaptive_write_handler [C1 192.168.1.144:557 ready socket-flow (satisfied (Path is satisfied), viable, interface: en0, scoped, ipv4, dns)] unregister notification for write_timeout failed

一开始,我以为这是报错了。经过各种上网的查询,得出结论,这个玩意好像没有啥用,就是一个提示他并不是报错。

然后我就又开始找,上面的代码可以连接上socket服务,但是不能接收消息,也不能发送消息,一开始以为是有什么参数没有配置,后来找了各种各样的 CocoaAsyncSocket 三方库的swift代码,发现从链接到发送再到接收都是这样,并没有缺少哪一段。

期间有的东西可能是swift版本不同,所以参数改变了:

代码中 dispatch_get_main_queue() 参数变成了 => DispatchQueue.main

其他的基本xcode都会有提示,跟着提示点 fix就可以了!

 

回归正题,这种方式各种失败所以我又开始了上网,去查找代码的问题。过程中发现了有人说需要继承 NSObject,最开始没有当一回事,后来成功之后发现可能是这个原因。

我又换了 socketio三方库,同样控制台还是现实上面那一段。

 

之后经过了各种查询,这几个搜索引擎可能都快被我问烦了。我终于找到一篇文章:https://zhuanlan.zhihu.com/p/40163604

这篇文章同样是使用 CocoaAsyncSocket 三方库。我抱着死马当活马医的态度,试了一下,成功了!他就这么成功了!你说气不气人!

之后我又各种总结,终于发现我原来的 socket类继承的是UiViewControll 而这个继承的就是 NSObject 。。。。

 

到此,我的iOS socket服务链接基本成功了!

粘贴一下代码:

SocketManage.swift 

//
//  SocketManage.swift
//  *****
//
//  Created by seventeen on 2021/7/19.
//

import Foundation
import UIKit
import CocoaAsyncSocket

class SocketManage: NSObject {
    
    static let shared = SocketManage()
    
    let serverPort: UInt16 = UInt16(557)//添加端口
    let serveripInput:String = "192.168.1.144"//添加IP
    var diction : NSDictionary?
    var clientSocket:GCDAsyncSocket!
    var noti: Notification?//用于接收到服务器回传数据后发送成功通知

    fileprivate var timer: Timer?
    
    private override init() {
        super.init()
        self.timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector:  #selector(timerAction), userInfo: nil, repeats: true)
        RunLoop.main.add(timer!, forMode: RunLoop.Mode.common)
        self.timer?.fireDate = Date.distantFuture
    }

    /**
     *计时器每秒触发事件
     **/
    @objc func timerAction() -> Void {
        sendMessage(self.diction as? [String : Any], type: 1001)
    }
    
    /**
     * 销毁计时器
     **/
    func destroyTimer(){
        self.timer?.invalidate()
        self.timer = nil
    }

    /**
     * 连接服务器
     **/
    
    func connectServer(){
        
        clientSocket = GCDAsyncSocket()
        clientSocket.delegate = self
//        clientSocket.delegateQueue = DispatchQueue.global()
        clientSocket.delegateQueue = DispatchQueue.main
        do {
            try clientSocket.connect(toHost: serveripInput, onPort: serverPort)
        } catch {
            print("try connect error: \(error)")
        }

    }
    /**
     * 消息数据包装
     **/
    
    /**
     * 字段   Length         type           boby
     * 长度   Int:4byte     Int:4byte    jsonString
     * 释义   boby长度       自定义类型       数据内容
     * 以上信息不固定和服务端提前定好解析回传数据也一样
     *  type  1001
     **/
    func sendMessage(_ serviceDic:[String:Any]?,type:Int){
       
        //print("type---> \(type)")
        
        var bodyDatas = Data()
        if serviceDic != nil{

           bodyDatas = try! JSONSerialization.data(withJSONObject: serviceDic!,options: .prettyPrinted)
            
        }else{
           bodyDatas.count = 4
        }

        var b:UInt32 = CFSwapInt32HostToBig(UInt32(type))
        let typeData = Data(bytes: &b, count: 4)
        
        print("typeData---------\(typeData.description)")

        //Length
        
        var len:UInt32 = CFSwapInt32HostToBig(UInt32(bodyDatas.count + 1))
        let lengthData = Data(bytes: &len, count: 4)
        var byteLengthData = Data()
        for bytesss in lengthData.reversed() {
            byteLengthData.append(bytesss)
        }

        //向服务器进行写数据
        clientSocket.write(lengthData, withTimeout: -1, tag: 0)
        clientSocket.write(typeData, withTimeout: -1, tag: 0)
        clientSocket.write(bodyDatas, withTimeout: -1, tag: 0)
    }
    
}

extension SocketManage:GCDAsyncSocketDelegate{
    
    /**
     * 连接服务器 成功
     **/
    func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) -> Void {
        
        print("Socket 连接服务器成功")
        //需要向服务器传递的数据
        let dic:[String : Any] = ["sessionId":"87878","udid":"HelloiOS"]
        //发送数据以及之前和服务器定好的类型
        sendMessage(dic, type: 1001)
        self.diction = dic as NSDictionary
        clientSocket.readData(withTimeout: -1, tag: 0)

        //连接成功后 启动定时器 发送心跳
//        timer?.fireDate = Date.distantPast
    }
    
    /**
     * 连接服务器 失败
     **/
    func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Swift.Error?) -> Void {
        //print("Socket 连接服务器失败: \(String(describing: err))")
        self.timer?.fireDate = Date.distantFuture
        connectServer()
    }
    
    /**
     * 处理服务器发来的消息
     **/
    
    func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) -> Void {

        //print("---Data Recv---")
        
        var byteArr:[UInt8] = []
        var lengthData:[UInt8] = []
        var typeBytesArr:[UInt8] = []
        var bodyBytesArr:[UInt8] = []
        var i = 0
        
        let type:Int = 0
        var length:UInt32 = 0
        let msg = String(data: data as Data, encoding: String.Encoding.utf8)
        print("收到了数据:\(msg)")
        for byte in data {
            if i < 4 {
                lengthData.append(byte)
            }
            let array : [UInt8] = lengthData
            let data = Data(array)
            length = UInt32(bigEndian: data.withUnsafeBytes { $0.pointee })
            if i >= 4 && i < 8{
                typeBytesArr.append(byte)
            }
            
            if i >= 8 && i < length + 7{
                bodyBytesArr.append(byte)
            }
            byteArr.append(byte)
            i += 1
        }
  
        //print("Bytes--->\(byteArr)")
        //print("lengthData ---> \(lengthData)")
        
        let bodyData = Data(bytes: bodyBytesArr, count: bodyBytesArr.count)
        print("收到了数据:\(bodyData)")
        print("---------------------------------------------")
//        //将二进制流转化为json数据
//        let bodyMs = JSON(bodyData)
//
//        let bodyDic = bodyMs.dictionaryObject
//        if bodyDic != nil{
//            print("\(data)")
//            print("body -->\(bodyMs)")
//        }
    
        if type == 6{
            //print(" ")
        }
        //解析完数据 发送通知 对应地方接收处理
        noti = Notification(name:NSNotification.Name(rawValue: "SocketManageNotification"), object: nil, userInfo: nil)
        NotificationCenter.default.post(noti!)
        //每次读完数据后,都要调用一次监听数据的方法
        clientSocket.readData(withTimeout: -1, tag: 0)
    }

}

这里面有包括 发送心跳 ,因为我还在测试,所以我将发送心跳的代码注释了!

 

说的不一定对,希望如果大佬看到了能指出错误,记录下来也希望对各位ios开发新手能有所帮助!

 

上一篇:CSharp中Socket网络编程(五)Socket状态检查与释放


下一篇:Socket - 基本流程