最近在作iOS上,基於UDP傳輸音視頻時遇到的一個問題,這邊紀錄一下:git
因爲考慮實時性比較高,因此採用了 CocoaAsyncSocket 的UDP框架來實現,將視頻切割成一幀幀的圖片發給服務端,不過,在發送圖片的過程當中,發現:github
當圖片大於9k大小時,會發送失敗;緩存
在didclose代理方法裏,會打印錯誤信息:Message too longapp
func udpSocketDidClose(_ sock: GCDAsyncUdpSocket, withError error: Error?) { print("udp close:\(error?.localizedDescription)") }
並且senddata成功或失敗的都跳過了,沒有執行框架
func udpSocket(_ sock: GCDAsyncUdpSocket, didSendDataWithTag tag: Int) { print("發送信息成功") }
其實就是數據太長,致使socket直接關閉了。。。socket
查了好些資料,發如今OS X上,因爲是由於:默認狀況下,OSX具備有限的最大是9216個字節的UDP包。async
這樣就阻止了超過大小的包的發送。ide
而後,有一種辦法,是經過終端讓系統增大限制數;測試
sudo sysctl -w net.inet.udp.maxdgram=65507
這樣執行完,在模擬器上運行,的確是能夠實現超過9k的圖片的發送,不過在真機上,就沒辦法了。。。spa
若是想查看udp其餘信息,這樣:
sudo sysctl -w net.inet.udp
不過,這種辦法,並非最終的解決辦法,因此不知道還有沒有更好的辦法呢。。。
參考資料:
=======================!!!!!!!解決了!!!!!!!!!!!!=======================
感謝github的大神 Noskthing 的幫助
參考:
https://github.com/robbiehanson/CocoaAsyncSocket/issues/535
https://github.com/robbiehanson/CocoaAsyncSocket/pull/536
方法:
修改GCDAsyncUdpSocket.m內文件,添加一段話
/** * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. * * The default maximum size of the UDP buffer in iOS is 9216 bytes. * * This is the reason of #222(GCD does not necessarily return the size of an entire UDP packet) and * #535(GCDAsyncUDPSocket can not send data when data is greater than 9K) * * * Enlarge the maximum size of UDP packet. * I can not ensure the protocol type now so that the max size is set to 65535 :) **/ int maximumBufferSize = 65535; status = setsockopt(socketFD, SOL_SOCKET, SO_SNDBUF, (const char*)&maximumBufferSize, sizeof(int)); if (status == -1) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error setting send buffer size (setsockopt)"]; close(socketFD); return SOCKET_NULL; } status = setsockopt(socketFD, SOL_SOCKET, SO_RCVBUF, (const char*)&maximumBufferSize, sizeof(int)); if (status == -1) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error setting receive buffer size (setsockopt)"]; close(socketFD); return SOCKET_NULL; }
位置就在:1988行左右開始
上面的這段代碼就是從源碼上修改緩存池最大限制,使可以傳輸超過9216的data,通過個人測試,在不超過64k的前提下,都是能夠發送的。
===========================update 2017.3.24 分片傳輸data =================
另外我寫了個demo,若是想用分片,也是能夠解決的
思路大概這樣:
發送端:
一、對要發送的圖片先處理:大於9000的分片屢次發送
二、每次發送的片帶上一個頭,三部分組成:分片標示符、頁碼數據、補充數據,且限定10位
a:分片標示符表示這段數據是分片,須要分片處理,好比用字符「flag」(後來我發現不加標識符好像也能夠,看我的了)
b:頁碼數據包含當前頁和總頁,二者用字符-分割(分割是爲了接收方法裏截取),總頁至關於索引總頁,好比有3段,就是0、一、2中的2,能夠用餘數理解。
c:若是前兩段不滿10位,不足用字符a補齊(a是舉例,能夠自行更換)
這樣好比:一張圖片有20k,會分紅3段發送,以下:
flag0-2aaa+分片數據
flag1-2aaa+分片數據
flag2-2aaa+分片數據
解釋:構成一個頭,而後再拼接上真實的圖片數據,兩部分組合進行發送。
接收端:
一、先定義一過全局可變data類型屬性(NSMutableData),用於封裝一段段分片,好比:
var showData:NSMutableData! = nil
二、每次在didReceive裏,先根據data和showData判斷是不是分片數據
三、若是不是分片,直接處理
四、若是是分片數據,提取頭部內容,根據索引,累加到showData裏,到所有結束後,處理顯示
放上兩段示例代碼:
發送端:
/// 將圖片數據分片發送 /// /// - Parameter imgData: <#imgData description#> func sendSmall(imgData:NSData) { let count = imgData.length/maxData var temp:Data? var startFlag:NSMutableData var length = 0 if count>0 { for index in 0...count { length = index == count ? imgData.length - index*maxData : maxData temp = imgData.subdata(with: NSRange(location: index*maxData, length: length)) //頭部加序列號 let str = getMaxLength(str: "flag\(index)-\(count)") startFlag = NSMutableData(data: str.data(using: .utf8)!) //序號和正文用\r\n分割 //startFlag.append(GCDAsyncSocket.crlfData()) startFlag.append(temp! as Data) clientSocket?.send(startFlag as Data, toHost: UDPClientViewController.host, port: UInt16(UDPClientViewController.port), withTimeout: -1, tag: 0) } }else{ clientSocket?.send(imgData as Data, toHost: UDPClientViewController.host, port: UInt16(UDPClientViewController.port), withTimeout: -1, tag: 0) } }
func getMaxLength(str:String) -> String { var result:String = str if str.characters.count<10 { let len = 10 - str.characters.count for _ in 0..<len { result.append("a") } } print(result) return result }
接收端:
/// 只要開始添加了 beginreceiving 這裏就能夠檢測到(這裏我就在一個裏面實現了 send 並 接收顯示) /// /// - Parameters: /// - sock: <#sock description#> /// - data: <#data description#> /// - address: <#address description#> /// - filterContext: <#filterContext description#> func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) { print("接收到\(address)的消息") if data.count<=maxData && showData == nil { if let img = UIImage(data: data){ imageShow.image = img }else{ print("data error") } }else{ let ten = NSData(data: data).subdata(with: NSRange(location: 0, length: 10)) var tenStr = String(data: ten as Data, encoding: .utf8) print("tenStr:\(tenStr)") if (tenStr?.contains("flag"))! { let imgData = NSData(data: data).subdata(with: NSRange(location: 10, length: data.count-10)) if showData == nil { showData = NSMutableData(data: imgData) }else{ showData.append(imgData) } tenStr = tenStr?.replacingOccurrences(of: "flag", with: "") tenStr = tenStr?.replacingOccurrences(of: "a", with: "") let dict:[String] = (tenStr?.components(separatedBy: "-"))! if dict.count>1 { let d1 = Int(dict[0]) let d2 = Int(dict[1]) if d1 == d2 { print(showData.length) if let img = UIImage(data: showData as Data){ imageShow.image = img showData = nil }else{ print("no img") } } } } } }