最近在做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=

这样执行完,在模拟器上运行,的确是可以实现超过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 = ; status = setsockopt(socketFD, SOL_SOCKET, SO_SNDBUF, (const char*)&maximumBufferSize, sizeof(int));
if (status == -)
{
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 == -)
{
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 =
if count> {
for index in ...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: -, tag: ) } }else{
clientSocket?.send(imgData as Data, toHost: UDPClientViewController.host, port: UInt16(UDPClientViewController.port), withTimeout: -, tag: ) } }
    func getMaxLength(str:String) -> String {
var result:String = str if str.characters.count< {
let len = - str.characters.count
for _ in ..<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: , length: ))
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: , length: data.count-))
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> {
let d1 = Int(dict[])
let d2 = Int(dict[])
if d1 == d2 {
print(showData.length)
if let img = UIImage(data: showData as Data){
imageShow.image = img
showData = nil
}else{
print("no img")
}
}
} } } }

CocoaAsyncSocket UDP发送数据超过包大小限制(Message too long)的更多相关文章

  1. Java使用UDP发送数据到InfluxDB

    最近在做压测引擎相关的开发,需要将聚合数据发送到InfluxDB保存以便实时分析和控制QPS. 下面介绍对InfluxDB的使用. 什么是InfluxDB InfluxDB是一款用Go语言编写的开源分 ...

  2. 通过 UDP 发送数据的简单范例

    package j2se.core.net.udp; import java.io.IOException;import java.net.DatagramPacket;import java.net ...

  3. TCP和UDP发送数据包的大小问题

    用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535-20-8=65507字节,其中20字节为IP包头长度,8字节为UDP包头长度.用sendto函数发送数据时,如果指的的数据长度 ...

  4. 使用 log4js UDP 发送数据到 logstash

    本文地址 http://www.cnblogs.com/jasonxuli/p/6532723.html 因为 nodejs 一般会部署在多台机器,并且每台机器会起多个进程,因此查看日志时往往要人工区 ...

  5. PL/SQL 调用JAVA使用UDP发送数据

    步骤如下 1.直接在SQL命令中写入JAVA代码(用SYS帐号执行,不然权限等太麻烦) create or replace and resolve java source named udp as i ...

  6. android 使用UDP发送数据 DatagramSocket 创建对象为null

    DatagramSocket socket=null; try { socket = new DatagramSocket();  //这里创建对象为空 } catch (SocketExceptio ...

  7. uip UDP server广播模式(client能够随意port,而且主动向client发送数据)

    眼下移植uip,发现UDP server模式下,必须指定本地port以及clientport,否则仅仅能讲clientport设置为0,才干接收随意port的数据,可是无法发送数据,由于此时clien ...

  8. Android(java)学习笔记80:UDP协议发送数据

    UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: 1 package cn.itcast_02; import java.io.IOException; import java.net. ...

  9. Android(java)学习笔记20:UDP协议发送数据

    1. UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: package cn.itcast_02; import java.io.IOException; import java.net ...

随机推荐

  1. js操作XML文件兼容IE与FireFox

    最近项目中用到了xml,需求是用户安装产品时先把一系列的数据保存到xml文件中,当执行到最后一步时才写入数据库,这样最大限度的减少了数据库的访问,于是不得不纠结在各浏览器的兼容性的问题(悲哀啊.... ...

  2. Git-忽略规则.gitignore生效

    Git中如果忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改根目录中 .gitignore 文件的方法,如下这个文件每一行保存了一个匹配的规则例如,忽略单个文件或者整个目录的文件: *.css ...

  3. iOS开发-UIActivityIndicatorView简单使用

    软件开发的时候经常会遇到半天才加载出来数据的情况,不管是程序写的烂,还是说本来网速比较慢,一般都都会给个提示让用户感觉到我们在努力的加载数据,iOS可以通过UIActivityIndicatorVie ...

  4. iOS开发-CocoaPods实战

    CocoaPods 是开发 OS X 和 iOS 应用程序的第三方库的依赖管理工具,如果是正常的开发不需要使用的第三方的代码,CocoaPods是不需要的,但是从实际情况上,为了提高开发效率,Coco ...

  5. jquery ajax 的 $.get()用法详解

    js文件 $(document).ready(function(){ $("form").submit(function(event) {event.preventDefault( ...

  6. laravel excel 导出表格

    1:创建导出文件,传入数据 $cover = [ ['第','一','行'], ['第','二','410000000000000000'.''],//防止数字过长导致格式乱码 例如身份证需在字段后加 ...

  7. 【转】深入理解line-height

    原文: http://www.cnblogs.com/dolphinX/p/3236686.html https://www.cnblogs.com/yangjie-space/p/4858132.h ...

  8. 转: git使用时让http记住帐号密码

    见 http://git.mydoc.io/?t=154710 https 方式每次都要输入密码,按照如下设置即可输入一次就不用再手输入密码的困扰而且又享受 https 带来的极速 按照以下设置记住密 ...

  9. Android MarsDaemon实现进程及Service常驻

    前段时间.就讨论过关于怎样让Service常驻于内存而不被杀死,最后的结论就是使用JNI实现守护进程,可是不得不说的是,在没有改动系统源代码的情况下,想真正实现杀不死服务,是一件非常难的事情.眼下除了 ...

  10. .geodatabase与gdb的相互转换

    .geodatabase长得是gdb的全称,确实它们有一定的关系,但也有区别. 简单认识一下 有人也问过我,gdb外表像个文件夹,是怎么实现的.gdb数据库是ESRI特有的数据库,它是一些数据集定义. ...