zoukankan      html  css  js  c++  java
  • CocoaAsyncSocket UDP发送数据超过包大小限制(Message too long)

    最近在做iOS上,基于UDP传输音视频时遇到的一个问题,这边纪录一下:

    由于考虑实时性比较高,所以采用了 CocoaAsyncSocket 的UDP框架来实现,将视频切割成一帧帧的图片发给服务端,不过,在发送图片的过程中,发现:

    当图片大于9k大小时,会发送失败;

    在didclose代理方法里,会打印错误信息:Message too long

     func udpSocketDidClose(_ sock: GCDAsyncUdpSocket, withError error: Error?) {
            print("udp close:(error?.localizedDescription)")
        }

    而且senddata成功或失败的都跳过了,没有执行

        func udpSocket(_ sock: GCDAsyncUdpSocket, didSendDataWithTag tag: Int) {
            print("发送信息成功")
        }

    其实就是数据太长,导致socket直接关闭了。。。

    查了好些资料,发现在OS X上,由于是因为:默认情况下,OSX具有有限的最大是9216个字节的UDP包。

    这样就阻止了超过大小的包的发送。

    然后,有一种办法,是通过终端让系统增大限制数;

    sudo sysctl -w net.inet.udp.maxdgram=65507

    这样执行完,在模拟器上运行,的确是可以实现超过9k的图片的发送,不过在真机上,就没办法了。。。

    如果想查看udp其他信息,这样:

    sudo sysctl -w net.inet.udp

    不过,这种办法,并不是最终的解决办法,所以不知道还有没有更好的办法呢。。。

    参考资料:

    1、UDP Message too long

    2、set max packet size for GCDAsyncUdpSocket

    3、GCDAsyncUDPSocket can not send data when data is greater than 9K?

    =======================!!!!!!!解决了!!!!!!!!!!!!=======================

    感谢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,如果想用分片,也是可以解决的

    思路大概这样:

    发送端:

    1、对要发送的图片先处理:大于9000的分片多次发送

    2、每次发送的片带上一个头,三部分组成:分片标示符、页码数据、补充数据,且限定10位

        a:分片标示符表示这段数据是分片,需要分片处理,比如用字符“flag”(后来我发现不加标识符好像也可以,看个人了)

        b:页码数据包含当前页和总页,两者用字符-分割(分割是为了接收方法里截取),总页相当于索引总页,比如有3段,就是0、1、2中的2,可以用余数理解。

        c:如果前两段不满10位,不足用字符a补齐(a是举例,可以自行更换)

    这样比如:一张图片有20k,会分成3段发送,如下:

    flag0-2aaa+分片数据

    flag1-2aaa+分片数据

    flag2-2aaa+分片数据   

    解释:构成一个头,然后再拼接上真实的图片数据,两部分组合进行发送。

    接收端:

    1、先定义一过全局可变data类型属性(NSMutableData),用于封装一段段分片,比如:

    var showData:NSMutableData! = nil

    2、每次在didReceive里,先根据data和showData判断是否是分片数据

    3、如果不是分片,直接处理

    4、如果是分片数据,提取头部内容,根据索引,累加到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)!)
       
                    //序号和正文用
    分割
                    //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)
    
            }
          
        
        
        }
    View Code
        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
        }
        
    View Code

    接收端:

    /// 只要开始添加了 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")
                            }
                        }
                    }
                    
                    
                }
    
            }
    
            
        }
    View Code
  • 相关阅读:
    QWrap简介之:EventW Event包装
    QWrap简介之:core_retouch 渲染原生类
    QWrap简介之:youa_retouch 项目个性
    QWrap简介之:Apps 应用 收获果实
    QWrap简介之:Wrap模式
    QWrap简介之:dom_retouch NodeW 勇士装甲
    Activity之间的数据传递
    OpenGL ES Tutorial for Android
    从零开始学习OpenGL ES之一 – 基本概念
    java自定义注解
  • 原文地址:https://www.cnblogs.com/yajunLi/p/6595509.html
Copyright © 2011-2022 走看看