聊一聊客户端项目内应用Socket碰到的情况

690 阅读5分钟

本题不讲基础,讲的是实际应用。首先项目内采取Socket,想必都是从 长连接 频繁数据通讯出发,但大多数人使用的时候都是采用了堵塞方式去创建Socket,堵塞方式:每产生一个连接,服务端就要开启一个线程来处理,没处理完成不能退出,客户端会一直在等待结果返回, 非堵塞方式:基于反应器模式,多路并发,多路复用,事件来了就处理,处理完成就归还,相信对线程熟悉的老哥们这时候肯定会想到了用线程池管理,没错,这就是非堵塞的优势之处。

举个例子

当前有100个客户端连接服务端,堵塞方式:就要打开100个线程来处理,不能重复回收复用,因为你要服务完你接待的上帝(客户端)才可以结束 非堵塞方式:只需要开30个线程,通过线程池管理,处理完成事件回收、复用 咳咳咳,看到这里大家应该明白这两种模式的好坏取舍了,当然,这对于客户端来说是看不到的,主体说的是服务端的性能了,有点跑题了!!

正式进入正文:

我们用Socket就是长链接频繁数据通讯,竟然有通讯,那么就离不开数据交互。不知道各位大大有没有接收过超过1024字节的数据呢?如一张动态图片、文件之类。 如果有,那么应该能猜测到小二我准备说的主题了,没有接收过的同学,请注意认真听讲了喂!!!

场景:服务端向客户端发送一张8M动态图片,客户端轮询读取接收,接收到通知UI出列刷新,听起来流程一点问题没有是不是? no!no!no! 同学,要是这样简单我就对不起铺垫了,服务端确实是整个图片发出去了,但是由于字节多大,通讯框架处于好心,主动帮我们把8M 的数据字节流拆成了N多个包发送给客户端,so 前面客户端接收到的只是某一个包,而不是完整的8M动态图

如图

前面过于纯洁的同学下课后来自觉来我这里领下作业!!! 言归正传,数据过大,被强行分包?那么我们要怎么处理呢?

聪明的童鞋可能想到了,那我们自己来分包处理,这样就不会处于被动了.这里又引发一个问题。 怎么分包?分几个包?客户端怎么知道那几个包组合起来是一个完整的包? 分包:我们先把数据转成字节流,在转成进制字符串,在进行字符串拆分即可

分几个包:通常客户端在读取的时候都会设置一个长度(readLine获取)所以这里我们也不用过于纠结,服务端1024字节或者其他来拆分都可以。

客户端怎么接收?

假设服务端把8m动态图分成了8个数据包下发?客户端接收后怎么判断是否完整包?

来,各位看官发挥一下想象力-_-

想一想我们转成了进制字符串,那我们可不可以在上面做一下手脚处理呢? 答案当然是 可以的啦!!

我们可以在没拆分前的进制字符串头和尾添加标识字符,例如:进制字符串是 88777222777 ,我们用AA来当作标识字符:AA88777222777AA。 这样看起来客户端是不是很明确的知道怎么去处理了呢?

客户端只要接受收据,碰到AA作为开头的数据包,就缓存起来放进队列或者自己的缓冲变量,等到遇见AA结尾的数据包,就整体拼接,剔除开头结尾的AA,这样不就是一个完整的数据包了么? ps:这里还有一个小问题, 有时候通讯频繁的时候一些不可知因素还容易产生数据缺失,所以我们上面的方法还有一点缺陷,我们不知道字节数对不对。 想想看,我们都能在没拆分前的进制字符加上AA标识,为什么不能把长度也加上去呢?

如图

如上图所示,我们可以和客户端约定好数据协议,这里有没有写过串口的童鞋呢?那么接触起来没有丝毫困难! 起始位+数据包长度+数据+结束位,这里的数据处理都是用进制字符哦,我们可以这样设置 起始位 2个字节+数据包长度 1个字节+数据 n个字节+结束位 2个字节,不熟悉字节和进制的童鞋,你要好好补一补基础了。 如上流程,那么客户端在拿到一个开头结尾都是AA的完整数据包之后,在取出第三个字节的数据包长度,判断一下数据包就可知道是否完整了 最后~ 情况当然不会那么少了,还有

粘包

断包

等等呢,小二今天还有事情,下次有时间在和各位大大具体分析了,bye~