本人第一次使用swift编写客户端,才疏学浅,请高手执教 :)
CocoaAsyncSocket Github: https://github.com/robbiehanson/CocoaAsyncSocket
这个应该是最好的socket client 第三方库
本文章主要介绍自定义协议,使用其他第三方协议可以忽略。
服务端我用的是Erlang写的,使用了{packet,4}选项,就是说每个数据包前4个字节(bigEndian)代表内容长度。
要点1:socket收发流程
mSocket.connect(toHost: "x.x.x.x", onPort: 9000, withTimeout: TimeInterval(1000))
public func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16)
sock.readData(withTimeout: -1, tag: 0)
public func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int)
parse_data(data: data)
sock.readData(withTimeout: -1, tag: 0)
... ...
循环收发
要点2: UInt32 与 Data 之间的转换
//UInt32 -> Data
let myuid:UInt32 = 1000
var uid = myuid.bigEndian
let data = Data(bytes: &uid, count: MemoryLayout.size(ofValue: uid))
//Data -> UInt32
let lenByte = data.subdata(in: 0..<PACKET )
let len = UInt32(bigEndian: lenByte.withUnsafeBytes { $0.pointee })
要点3:为了避免粘包,递归处理数据包
服务器1秒内发送多个数据包,使用readData会合并起来(如果使用别的协议,有结束符,可以使用别的函数避免这个问题),我采用erlang最常用的尾递归方式,详见下面代码: parse_data ,
源码:
Socket处理类:MySocket.swfit
import Foundation
import CocoaAsyncSocket
let PACKET = 4
public class MySocket: NSObject , GCDAsyncSocketDelegate {
var mSocket: GCDAsyncSocket!
var connectStatus = 0
override init() {
super.init()
mSocket = GCDAsyncSocket()
mSocket.delegate = self
mSocket.delegateQueue = DispatchQueue.main
}
public func send(){
let cmd:UInt8 = 2
let myuid:UInt32 = 1000
var uid = myuid.bigEndian
let str = UIDevice.current.identifierForVendor?.uuidString
let content = str?.data(using: String.Encoding.utf8)
var d = Data()
d.append(cmd)
d.append(Data(bytes: &uid, count: MemoryLayout.size(ofValue: uid)))
d.append(content!)
let contentlen = UInt32(d.count)
var len = contentlen.bigEndian
d.insert(contentsOf: Data(bytes: &len, count: MemoryLayout.size(ofValue: len)), at: 0)
mSocket.write(d, withTimeout: TimeInterval(1000), tag: 0)
}
public func connect() {
do {
connectStatus = 0
try mSocket.connect(toHost: "x.x.x.x", onPort: 9000, withTimeout: TimeInterval(1000))
} catch {
print("conncet error")
}
}
public func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) {
parse_data(data: data)
sock.readData(withTimeout: -1, tag: 0)
}
public func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) {
sock.readData(withTimeout: -1, tag: 0)
print("success")
}
public func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Error?) {
print("disconnect")
}
public func parse_data(data:Data){
let lenByte = data.subdata(in: 0..<PACKET )
let contentLength = Int(UInt32(bigEndian: lenByte.withUnsafeBytes { $0.pointee }))
let contentByte = data.subdata(in: PACKET..<contentLength+PACKET)
switch contentByte[0] {
case 1:
let flagByte = contentByte[1]
let uidByte = contentByte.subdata(in: 2..<contentByte.count)
print(flagByte, UInt32(bigEndian: uidByte.withUnsafeBytes { $0.pointee }))
break;
case 2:
let flagByte = contentByte[1]
print(flagByte)
break;
default:
print("unknow cmd")
}
if contentLength+PACKET < data.count {
parse_data(data: data.subdata(in: contentLength+PACKET..<data.count))
}
}
}
在 AppDelegate.swift 中定义全局变量,并初始化
import UIKit
var mySocket: MySocket?
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = Color.white
mySocket = MySocket()
//....
网友评论