请教,郁闷死了AsyncSocketjs接收json数据大的数据时就js接收json数据的不完整

安全检查中...
请打开浏览器的javascript,然后刷新浏览器
< 浏览器安全检查中...
还剩 5 秒&相关文章推荐
最近不知道怎么回事,项目里边使用GCDAsyncSocket上传图片,接收回执是会出现丢包的现象,后来发现只要是丢包了,在- (void)socket:(GCDAsyncSocket *)sock d...
首先 下载源代码
倒入runloop文件夹下的 四个文件即可
AsyncSocket *socket=[[AsyncSocketalloc]initWithDelegate:self];
最近在使用GCDAsyncSocket进行socket通信的工作,总是莫名的
下面的代码是一个实例化SocketChannel的过程:
SocketChannel channel = SocketChannel.open();
//要设置连接超时
1、当你write完数据后,
[_socket writeData:header withTimeout:ABSOCKET_TIMEOUT tag:tagg];
最好不要立即调用
IOS开发中,使用 GCDAsyncUdpSocket接收广播包,折腾半天没有反应。其实很简单,
bind port时不要指定 interface!
[mGCDAsyncUdpSocket
做项目的时候用到了GCDAsyncUdpSocket开源库,所以在此总结下它的用法,作为笔记;GCDAsyncUdpSocket简介GCDAsyncUdpSocket开源类库是以苹果的GCD多任务处理...
在做一个App的时候,有一个需求是服务端在局域网当中发送广播数据,然后连接到此局域网当中的客户端接收到广播数据后,对广播数据做出相应地回复。在这个需求当中广播的唯一的好处就是客户端连接到局域网当中不需...
先去github的网站下载最新的包,然后先看看介绍。
/robbiehanson/CocoaAsyncSocket/wiki/Intro_GCDAsyncSo...
GCDAsyncSocket类库,IOS下TCP通讯使用心得
发表于 2013
年 7 月 25 日 由 水德星君
关于在IOS下使用Socket进行通讯的技术文章也许诺很久了,今...
他的最新文章
讲师:董晓杰
讲师:姚远
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)即时通讯下数据粘包、断包处理实例(基于CocoaAsyncSocket) - 简书
即时通讯下数据粘包、断包处理实例(基于CocoaAsyncSocket)
本文旨以实例的方式,使用CocoaAsyncSocket这个框架进行数据封包和拆包。来解决频繁的数据发送下,导致的数据粘包、以及较大数据(例如图片、录音等等)的发送,导致的数据断包。
本文实例Github地址:。
注:文章内容属于应用的范畴,内容相对简单易懂。给大家对数据包的处理提供了一个思路, 希望能抛砖引玉。它是楼主CocoaAsyncSocket系列Read篇解析的一个前置插曲,至于详细的实现原理,作者会在后续的文章中写出。
一、什么是粘包?
经常我们发现,如果用客户端同一时间发送几条数据,而服务端只能收到一大条数据,类似下图:
如图,由于传输的过程为数据流,经过TCP传输后,三条数据被合并成了一条,这就是数据粘包了。
那么为什么会造成粘包呢?
原来这是因为TCP使用了优化方法(Nagle算法)。它将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这么做优点也很明显,就是为了减少广域网的小分组数目,从而减小网络拥塞的出现。
具体的内容感兴趣的可以看看这两篇文章:
而UDP就不会有这种情况,它不会使用块的合并优化算法。这里说到了就顺便提一下,由于它支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息)。
当然除了优化算法,TCP和UDP都会因为下面两种情况造成粘包:
发送端需要等缓冲区满才发送出去,造成粘包
接收方不及时接收缓冲区的包,造成多个包接收。
二、什么是断包?
断包应该还是比较好理解的,比如我们发送一条很大的数据包,类似图片和录音等等,很显然一次发送或者读取数据的缓冲区大小是有限的,所以我们会分段去发送或者读取数据。类似下图:
无论是粘包还是断包,如果我们要正确解析数据,那么必须要使用一种合理的机制去解包。这个机制的思路其实很简单:
我们在封包的时候给每个数据包加一个长度或者一个开始结束标记。
然后我们拆包的时候就能区分每个数据包了,再按照长度或者分解符去分拆成各个数据包。
Talk is cheap. Show me the code
三、实例:基于CocoaAsyncSocket的封包,拆包处理。
开始动手之前,我们需要去理解下面这几个方法
//读取数据,有数据就会触发代理
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)
//直到读到这个长度的数据,才会触发代理
- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)
//直到读到data这个边界,才会触发代理
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)
还记得我们之前讲:中提到过,这个框架每次读取数据,必须手动的去调用上述这些read方法,而我们之前的实现思路是,第一次连接成功的代理触发后调用:
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)
之后每次收到消息之后,都在去调用一次这个方法,超时为-1,即不超时。这样我们每次收到消息,都会即时触发我们读取消息的代理:
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
然而这么做显然没有考虑数据的拆包,如果我们一条一条的发送文字信息,自然没什么问题。如果我们一次发送数条,或者发送大图片。那么问题就出来了,我们解析出来的数据显然是不对的。
这时候我们就需要另外两个read方法了,一个是读取到指定长度,另一个是读取到指定边界。我们通过自己定义的数据边界,去调用这两个方法,而触发的读取代理,得到的数据才是正确的一个包的数据。
所以我们的核心思路有了:
封包的时候给每个包的数据加一个标记,来标明数据的长度和类型(类型显然是需要的,我们需要知道它是文本、图片、还是录音等等,来用正确的方式处理这个数据)。
拆包的时候,先获取到我们给每个包的标记,然后根据标记的数据长度,去获取数据。最后再根据标记的类型去处理数据。(文字输出、图片展示、录音播放等等)。
接着我们可以开始动手了:这里我们首先需要一个服务端,一个客户端。为了简单,我们都用OC来实现。
其中我们客户端用手机,服务端我们用Xcode模拟器。(由于Xcode只能同一时间运行一个模拟器...)
这里我们用客户端封包发送数据,然后服务端拆包解析数据。
我们先来看看客户端的代码:
NSString * Khost = @"10.10.100.48";
static const uint16_t Kport = 6969;
//建立连接
- (BOOL)connect
[gcdSocket connectToHost:Khost onPort:Kport error:nil];
初始化略过了,大家可以看看github中的代码,这里需要说的是,为了连接上本机的服务端,我们这里的host为服务端的IP地址:
端口为6969(只需和服务端accpet端口一致即可)。
注意:如果大家要运行github上的demo,只需修改这个host地址即可,把它改成你电脑(服务端)的IP地址。
接着我们来看看write方法,我们在该方法中进行封包:
//发送消息
- (void)sendMsg
NSData *data
= [@"你好" dataUsingEncoding:NSUTF8StringEncoding];
NSData *data1
= [@"猪头" dataUsingEncoding:NSUTF8StringEncoding];
NSData *data2
= [@"先生" dataUsingEncoding:NSUTF8StringEncoding];
NSData *data3
= [@"今天天气好" dataUsingEncoding:NSUTF8StringEncoding];
NSData *data4
= [@"吃饭了吗" dataUsingEncoding:NSUTF8StringEncoding];
[self sendData:data :@"txt"];
[self sendData:data1 :@"txt"];
[self sendData:data2 :@"txt"];
[self sendData:data3 :@"txt"];
[self sendData:data4 :@"txt"];
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"test1" ofType:@"jpg"];
NSData *data5 = [NSData dataWithContentsOfFile:filePath];
[self sendData:data5 :@"img"];
- (void)sendData:(NSData *)data :(NSString *)type
NSUInteger size = data.
NSMutableDictionary *headDic = [NSMutableDictionary dictionary];
[headDic setObject:type forKey:@"type"];
[headDic setObject:[NSString stringWithFormat:@"%ld",size] forKey:@"size"];
NSString *jsonStr = [self dictionaryToJson:headDic];
NSData *lengthData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *mData = [NSMutableData dataWithData:lengthData];
[mData appendData:[GCDAsyncSocket CRLFData]];
[mData appendData:data];
//第二个参数,请求超时时间
[gcdSocket writeData:mData withTimeout:-1 tag:110];
- (NSString *)dictionaryToJson:(NSDictionary *)dic
NSError *error =
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
总共上述两个方法,也很简单,我们发送了6条数据,前5条为文本形式,最后一条是一个20多M的图片。当我们点击发送的时候会触发这个方法,这6条数据会被同时发出。
这里我们来看看我们是如何封包的:
我们定义了一个headDic,这个是我们数据包的头部,里面装了这个数据包的大小和类型信息(当然,你可以装更多的其他标识信息。)然后我们把它转成了json,最后转成data。
然后我们把这个head拼在最前面,接着拼了一个:[GCDAsyncSocket CRLFData]
这个是什么呢?其实它就是一个\r\n。我们用它来做头部的边界。(又或者我们可以规定一个固定的头部长度,来作为边界,这里仅仅是提供给大家一个思路)。
最后我们把真正的数据包给拼接上。
注:如果你想的更远的话,甚至可以在结尾,再拼一个包结束的标识符,后面我们会讲到为什么可以这么做。这里暂时先这样。
就这样,我们完成了数据的封包和发送。
客户端有了,接着我们来看看服务端是如何来拆包的:
首先我们需要监听本机6969端口。(完整代码可以见github)
static const uint16_t Kport = 6969;
//等待连接
- (BOOL)accept
NSError *error =
BOOL isSuccess =
[gcdSocket acceptOnPort:Kport error:&error];
if (isSuccess) {
NSLog(@"监听成功6969端口成功,等待连接");
return YES;
NSLog(@"监听失败,原因:%@",error);
return NO;
当客户端连接上来后,调用成功接收到客户端连接的代理方法:
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
NSLog(@"接受到socket连接");
[_sockets addObject:newSocket];
[newSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:110];
这里需要注意的是,成功接收到连接后,调用代理我们必须把新生成的这个newSocket保存起来,如果它被销毁了,那么连接就断开了,这里我们把它放到了一个数组中去了。这里需要注意的是,成功连接后,我们就调用了:
[newSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:110];
还记得我们封包的时候,数据包头部之后拼了这么一个分解符data。这样,当有数据包传输过来我们就能获取到这个数据包的头部(后面的信息先不读取)。
接着我们来看看服务端的read代理方法是如何拆包的:
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
//先读取到当前数据包头部信息
if (!currentPacketHead) {
currentPacketHead = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:nil];
if (!currentPacketHead) {
NSLog(@"error:当前数据包的头为空");
//断开这个socket连接或者丢弃这个包的数据进行下一个包的读取
NSUInteger packetLength = [currentPacketHead[@"size"] integerValue];
//读到数据包的大小
[sock readDataToLength:packetLength withTimeout:-1 tag:110];
//正式的包处理
NSUInteger packetLength = [currentPacketHead[@"size"] integerValue];
//说明数据有问题
if (packetLength &= 0 || data.length != packetLength) {
NSLog(@"error:当前数据包数据大小不正确");
NSString *type = currentPacketHead[@"type"];
if ([type isEqualToString:@"img"]) {
NSLog(@"图片设置成功");
self.recvImg.image = [UIImage imageWithData:data];
NSString *msg = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"收到消息:%@",msg);
currentPacketHead =
[sock readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:110];
这个方法也很简单,我们判断,如果currentPacketHead(当前数据包的头部)为空,则说明这次读取,是一个头部信息,我们去获取到该数据包的头部信息。并且调用下一次读取,读取长度为从头部信息中取出来的数据包长度:
[sock readDataToLength:packetLength withTimeout:-1 tag:110];
这样当GCDAsyncSocket中数据缓冲区长度达到我们需要读取的length就能触发代理方法的第二次回调。(具体原理实现会在楼主的GCDAsyncSocket解析的后续系列Read篇中去讲,敬请期待)。这时候因为currentPacketHead不为空,所以我们就知道是去获取一个数据包,我们从头部信息中拿到数据包的类型,如果是文本或者图片,则分别输出或展示到屏幕上。读取完成后我们再次调用:
[sock readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:110];
这样就开始了下一个数据包的头部信息读取。就这样,整个数据拆包的处理就完成了。
接着我们来讲讲我们之前所说的为什么可以在数据包之后加一个结束标识符。我们数据很可能在传输的过程中,丢失了一部分,或者头部信息不可读,导致我们无法正常读取这个数据包。可能我们会有一个应用场景,当出现错误包的时候,我们就直接抛弃掉它,直接开始下一个数据包的读取(当然现实中,我们往往是需要重新发送,这里仅仅是举一个应用场景)。这样这个结束标识符就起作用了,我们可以直接把数据读取到这个错误包的结束标识处,不做任何处理,这样相当于丢弃掉这个错误包了。
最后我们来看看运行效果:
我们客户端手机连接上服务器后,点击发送,发出我们上述客户端写的6条数据,在我们服务端,按照顺序接受到数据如图:
写在结尾:
本来不打算写应用篇的,但是很多朋友在问数据包相关的内容,而且正好之后的Read篇会涉及到这些,所以就当为了后面的内容做一个铺垫吧。
关于IM的路还有很长,路漫漫其修远兮,吾将上下而求索。
千里之行,始于足下
使用CocoaAsyncSocket这个框架进行数据封包和拆包。来解决频繁的数据发送下,导致的数据粘包、以及较大数据(例如图片、录音等等)的发送,导致的数据断包。一、什么是粘包? 经常我们发现,如果用客户端同一时间发送几条数据,而服务端只能收到一大条数据,类似下图: 如图,...
写在开始之前 这篇文章的由来是作者以前在看CocoaAsyncSocket一时兴起写的一个即时通讯小demo的介绍,内容包含心跳检查,粘包断包处理,多用户并发调度,用户间消息传送等。最近由于在搞一个sockes5的项目。重新整理了一下CocoaAsyncSocket方面的东...
在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的。因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小、数据量小的数据...
简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具。tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,...
Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调导致了样板模式, 使用Spring Cloud开发人员可以快速地支持实现这些模式的服务和应用程序。他们将在任何分布式...
十一出游 晨行露为霜,道傍百花香。 雾隐龙江岸,烟藏五合光。 遥乡秋色碧,近舍稻花黄。 漫看农家乐,欣欣国运昌。
目】《情绪急救》 【课程领拆】王志凤(嘟嘟)老师 【上课时间】日 【作
业】 作业一:练习有效道歉的6个成分。可以是自己的实际情况,也可供以用下列情景来进行案例练习: 小李之前开了一个好朋友的玩笑,说他是个花心的人。朋友说过不喜欢她这样说,但...
我做过最好的事, 就是遇见你, 但从此以后, 你和你的声色犬马, 我和我的各安天涯!
problemGivenanarrayofintegers,returnindicesofthetwonumberssuchthattheyadduptoaspecifictarget.Youmayassumethateachinputwouldhaveexactlyone...
爨底下村位于北京西郊门头沟区斋堂镇,国家A级景区。我们此次算是慕名而去的。 “爨”,念cuàn,为使“爨”字好写、好记,有一个顺口溜:“兴字头,林字腰、大字下面架火烧”。 爨底下村名声鹊起源于贺岁大片《手机》和《投名状》吧,此村子是影片的外景拍摄地,使得古朴风貌的爨底下村名...137被浏览21995分享邀请回答/2011/03/http-web-server-file-upload-on-iphone.html 251 条评论分享收藏感谢收起61 条评论分享收藏感谢收起

我要回帖

更多关于 串口接收数据 的文章

 

随机推荐