由於第一次接觸 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開發新手能有所幫助!
