长链接NIO
长链接NIO
推送
所有需要客户端被动接收信息的功能模块,都可以用推送实现,比如A想给B发消息,A调服务器接口,服务器只是存数据,它调推送的接口,推送去去找B。推送用的是xmpp协议, 是一种基于TCP/IP的协议, 这种协议更适合消息发送
- socket 套接字, 发送和接收网络请求
- 长连接 keep-alive, 服务器基于长连接找到设备,发送消息
- 心跳包 , 客户端会定时(30秒一次)向服务器发送一段极短的数据,作为心跳包, 服务器定时收到心跳,证明客户端或者,才会发消息.否则将消息保存起来,等客户端活了之后(重新连接),重新发送.
- 网络状态变化
手机网络和WIFI网络切换、网络断开和连上等情况有网络状态的变化,也会使长连接变为无效连接,需要监听响应的网络状态变化事件,重新建立Push长连接。
关于心跳
- 客户端网络空闲5秒没有进行写操作是,进行发送一次ping心跳给服务端;
- 客户端如果在下一个发送ping心跳周期来临时,还没有收到服务端ping的心跳应答,则失败心跳计数器加1;
- 每当客户端收到服务端的ping心跳应答后,失败心跳计数器清零;
- 如果连续超过3次没有收到服务端的心跳回复,则断开当前连接,在5秒后进行重连操作,直到重连成功,否则每隔5秒又会进行重连;(不对吧)。死链check 时间应该是心跳间隔 + 心跳消息超时
长连接会不会耗流量?
维持长连接并不是连接在那儿就一直消耗流量,只是隔段时间进行“心跳”来保持连接,而一次心跳流量是可以做得很小的。总之,长连接的方式一方面实时性好,另一方面,比轮询更少的消耗流量。项目是5秒发一次
心跳包和轮询的区别
轮询是为了获取数据,而心跳是为了保活TCP连接。
轮询得越频繁,获取数据就越及时,心跳的频繁与否和数据是否及时没有直接关系。
轮询比心跳能耗更高,因为一次轮询需要经过TCP三次握手,四次挥手,单次心跳不需要建立和断开TCP连接。
I/O 是什么?
程序内部和外部进⾏行数据交互的过程,就叫输入输出。
程序内部是谁?内存
程序外部是谁?
⼀般来说是两类:本地文件和网络。
也有别的情况,比如你和别的程序做交互,和你交互的程序也属于外部,但一般来说,就是文件和网络这么两种。
从文件里或者从网络上读数据到内存里,就叫输入;
从内存里写到文件里或者发送到网络上,就叫输出
Java I/O 作⽤用只有一个:和外界做数据交互
常见的IO
答:字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer,如果字符流就得有刷新动作。
- 打印流:将各种数据类型的数据原样打印,PrintStream 、PrintWriter
- 对象流:它的写方法是ObjectOutputStream,读方法是ObjectInputStream。它主要操作的是对象,而对象中也能封装数据,所以它也具备操作基本数据类型的方法。被它操作的对象必须是实现了序列化的对象也就是Serializable接口,但是输入流还多支持一种Externalizable 接口的对象。持久化
- DataStream:可以用于操作基本数据类型的数据的流对象:DataInputStream与DataOutputStream,它主要的特点就是操作基本数据类型,特色方法writeUTF、readUTF
- 字节数组流:ByteArrayInputStream 、ByteArrayOutputStream,因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭,而且关闭也是无效的。
- 文件流:FileReader
字符流
1 |
|
NIO
NIO vs IO区别
NIO vs IO之间的理念上面的区别(NIO将阻塞交给了后台线程执行)
- IO是面向流的,NIO是面向缓冲区的
Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;NIO则能前后移动流中的数据,因为是面向缓冲区的 - IO流是阻塞的 NIO流是不阻塞的
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了
Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
IO是阻塞式的,NIO是非阻塞式的,所有数据都是用缓冲区进行处理的。在读取数据时,它是直接读到缓冲区中;在写入数据时,它也是写入到缓冲区中。任何时候访问 NIO 中的数据,我们都是通过缓冲区进行读写操作。
- 看到Content-Encoding: gzip时,需要将Body数据先解压缩,才能得到真正的数据。压缩的目的在于减少Body的大小,加快网络传输。
- 同步和异步的区别就在于是否等待IO执行的结果。好比你去麦当劳点餐,你说“来个汉堡”,服务员告诉你,对不起,汉堡要现做,需要等5分钟,于是你站在收银台前面等了5分钟,拿到汉堡再去逛商场,这是同步IO。
你说“来个汉堡”,服务员告诉你,汉堡需要等5分钟,你可以先去逛商场,等做好了,我们再通知你,这样你可以立刻去干别的事情(逛商场),这是异步IO。
很明显,使用异步IO来编写程序性能会远远高于同步IO,但是异步IO的缺点是编程模型复杂。想想看,你得知道什么时候通知你“汉堡做好了”,而通知你的方法也各不相同。如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步IO的复杂度远远高于同步IO。
Netty
有一个接口监听。监听里有建立连接,收到消息,完成等各种回调,收到后解析
- Netty是一个异步非阻塞的事件驱动型的网络应用程序框架。是一个高性能、方便开发的NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议。Netty大大简化了网络编程比如TCP和UDP的 Socket的开发。
- IO是阻塞式的,NIO是非阻塞式的,所有数据都是用缓冲区进行处理的。在读取数据时,它是直接读到缓冲区中;在写入数据时,它也是写入到缓冲区中。任何时候访问 NIO 中的数据,我们都是通过缓冲区进行读写操作。
- 当客户端与服务器建立起连接后,ChannelHandler的方法是被网络event(这里的event是广义的)触发的,由ChannelHandler直接处理输入输出数据,并传递到管道中的下一个ChannelHandler中。
- 通过Channel或者ChannelHandlerContext发生的请求/响应event 就是在管道中ChannelHandler传递。
ChannelInboundHandler
- ChannelInboundHandler对从客户端发往服务器的报文进行处理,一般用来执行解码、读取客户端数据、进行业务处理等;ChannelOutboundHandler对从服务器发往客户端的报文进行处理,一般用来进行编码、发送报文到客户端。
- 一般就是继承ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter,因为Adapter把定制(custom) ChannelHandler的麻烦减小到了最低,Adapter本身已经实现了基础的数据处理逻辑(例如将event转发到下一个handler),你可以只重写那些你想要特别实现的方法。
- 和NIO一样,读取数据时,它是直接读到缓冲区中;在写入数据时,它也是写入到缓冲区中。在TCP/IP中,NETTY会把读到的数据放到ByteBuf的数据结构中。所以这里读取在ByteBuf的信息,得到服务器返回的内容。
- ChannelPipeline作为放置ChannelHandler的容器,采用了J2EE的 拦截过滤模式,用户可以定义管道中的ChannelHandler以哪种规则去拦截并处理事件以及在管道中的ChannelHandler之间如何通信。每个Channel都有它自己的Pipeline,当一个新的Channel被创建时会自动被分配到一个Pipeline中。
1 |
|
以上,就是一个供Android客户端使用的
ChannelHandler
,可以通过实现具体的OnDataReceiveListener
来异步地获得服务器返回的 数据。NettyClient的实现 以上仅仅是展示了如何处理服务器返回的数据。建立连接、发送消息以及心跳包的功能还没进行封装。
在2.接口的定义 里面已经定义好了NettyClient应该具备哪些行为,现在进行具体的实现。 主要的实现思路是:
- 构建Bootstrap,其中包括设置好ChannelHandler来处理将来接收到的数据(详见Android开发之使用Netty进行Socket编程(二) )。由Boostrap建立连接。通过
channel.writeAndFlush(constructMessage(sendMsg)).sync()
发送消息。这些工作都在子线程完成。 - 在子线程 建立连接并向服务器发送请求,这里采用了
HanlderThread
+Handler
的方案。通过Looper
依次从Handler
的队列中获取信息,逐个进行处理,保证安全,不会出现混乱。 - 心跳包的发送通过
handleMessage(Message msg)
中的死循环进行不间断地发送。 NettyClientHandler
的实现中我们已经知道,当Netty异常时会触发statusListener.onDisconnected();
,NettyClient中,onDisconnected()方法会进行重连操作。 接收到服务器返回的消息时,会在主线程中触发onDataReceiveListener .onDataReceive(mt, json);
。
5. 外部通过单例模式进行调用。