Socket 与 Android Socket AIO 库 Naga 的介绍
2015-11-18 17:44
399 查看
Socket介绍
Socket相关概念
应用场景
非阻塞IO复用
性能比较
Naga Socket 库
Nage 运行构架图
架构介绍
架构流程
Naga Socket Client 代码
Naga Socket Server 代码
小结
最近项目要求Android客户端使用Socket,所以把Android平台能够使用的Socket调研了一下:
异步: 是指应用进程触发IO操作后,就去执行其它任务,当有内核进程通知应用进程IO状态发生改变,应用进程根据最新的状态进行IO操作。
阻塞: 是指当IO状态改变后,进行相关IO操作时,如果IO被占用,一直处于等待,知道IO可用为止。
非阻塞: 是指当IO状态改变后,进行相关IO操作时,如果IO被占用,马上返回操作结果值,当IO就绪时,IO函数会通知最新的IO状态,从而继续进行IO操作。
了解以上几个概念之后,通过不同的组合,就会形成Socket的几个种类:BIO、NIO、AIO
同步阻塞(BIO): 代码行为模式为一个连接在一个线程中运行,在每个线程中如果IO处于阻塞状态,就是指一直等待IO准备就绪,知道可以继续进行操作。可用线程池提高处理性能。
同步非阻塞(NIO): 代码行为模式为一个请求一个线程。每一个连接通道都会注册到多路复用器上,多路复用器会轮询所有连接通道,发现有IO请求时会启动一个线程进行处理。多路复用器需要再一个单独的线程中去执行轮询操作。
异步阻塞(NIO): 代码行为模式为当应用发起IO操作后,马上就会受到状态回复,当内核处理完IO操作后,通知应用程序IO最新状态。在OS内核层,系统会调用select系统调用对IO进行处理,select会同时监听多个文件句柄,提高了并发性,但是对于每个文件句柄的读写操作时阻塞的。
异步非阻塞(AIO):代码行为模式为应用进程发起一个IO操作后立即返回,等内核处理完IO操作后,会通知应用进程IO数据,应用进程取得数据进行处理即可。
如果理解不太清楚,可以通过下面例子进行辅助理解:
同步阻塞: 小明去书店买书,然后一直在柜台前等着,并且一直询问:有书吗?有书吗?……
同步非阻塞: 小明去书店买书,跟柜台说完后就去网吧玩了,玩一会就跑回柜台问一下:书准备好了吗?
异步阻塞:小明给书店打电话说要定新书,当在网吧玩时,书店打来电话,说有新书了,小明自己去拿书。
异步非阻塞:小明给书店打电话说定新书,当在网吧玩时,书店打电话说,有新书到了,并派人将新书送过来。
NIO:适用于连接数多且为短链接,轻数据量的服务器,比如通讯服务器,编程方式复杂,JDK1.4中引入。
AIO:适用于连接数少且为长连接,重数据量的服务器,比如视频服务器,充满在JDK1.7中引入,所以Android系统中,只用5.0+版本才支持AIO。
select:通过设置或存放文件句柄标志位数据结构来处理IO,有如下缺点:
单线程可以监控的fd数量有限,默认为2048个,修改需要改动内核常量设置。
根据fd数量的大小决定,用户态和内核态之间传输效率
轮询起扫描时占用大量CPU计算时间
多线程同步机制的处理
poll:与select一样,将用户态的数据复制到内核中,然后遍历所有句柄,如果有就绪IO则将此IO放入等待队列中,如果没有准备就绪的IO,则挂起轮询进程,当IO被唤醒时再次启动轮询进程遍历fd表。
在poll中,fd使用链表存储,所以遍历的fd数量没有限制
用户态和内核态传输效率依然取决于fd数量
报告fd就绪后未被处理,下次poll时会再次报告该fd
epoll:epoll支持水平触发(Level-Triggered,支持阻塞和非阻塞模式)和边缘触发(Edge-Triggered,只有非阻塞模式)。
它只通知应用进程那些fd刚刚变为就绪状态,并且只会通知一次。
对于fd数据结构复制问题,epoll采用mmap共享内存来减少复制。
epoll通过向内核注册fd的方式来异步接收IO状态变化,
连接数受内存大小影响,1G内存可打开10万左右的fd
![](http://img.blog.csdn.net/20151118170007851)
channel实体:包括建立当前任务时的地址、端口号。
channel状态处理接口:包括readable、writeable、connected、accepted等
socket读写操作类:SocketWriter、SocketReader
Packet队列:包括当前任务的收发Packet,Selcetor遍历此包队列的信息,进行处理。
利用NIOServer的openSocket建立连接,并加入channelResponder到任务队列中。
channelResponder的建立过程中,会包括host address、port、channel、channel在selector重点的注册、packet queue以及相关接口,封装在实现Runable接口的类中。
NIOServer会在主线程中开启轮询操作。首先轮询是否在Task Queue中存在任务元组,若存在,唤醒selector遍历其中的Packet Queue对数据进行IO操作,直到Packet Queue为空后,将selector挂起。
下一篇,要对Naga库中的Socket+SSL功能进行介绍,实现Socket的加密通信功能。
Socket相关概念
应用场景
非阻塞IO复用
性能比较
Naga Socket 库
Nage 运行构架图
架构介绍
架构流程
Naga Socket Client 代码
Naga Socket Server 代码
小结
最近项目要求Android客户端使用Socket,所以把Android平台能够使用的Socket调研了一下:
Socket介绍
Socket分为如下几种:BIO、NIO、AIO,在了解每种Socket之前,首先要了解几个概念:Socket相关概念
同步: 是指应用进程触发IO操作后,等待或者轮询查看IO状态是否改变。异步: 是指应用进程触发IO操作后,就去执行其它任务,当有内核进程通知应用进程IO状态发生改变,应用进程根据最新的状态进行IO操作。
阻塞: 是指当IO状态改变后,进行相关IO操作时,如果IO被占用,一直处于等待,知道IO可用为止。
非阻塞: 是指当IO状态改变后,进行相关IO操作时,如果IO被占用,马上返回操作结果值,当IO就绪时,IO函数会通知最新的IO状态,从而继续进行IO操作。
了解以上几个概念之后,通过不同的组合,就会形成Socket的几个种类:BIO、NIO、AIO
同步阻塞(BIO): 代码行为模式为一个连接在一个线程中运行,在每个线程中如果IO处于阻塞状态,就是指一直等待IO准备就绪,知道可以继续进行操作。可用线程池提高处理性能。
同步非阻塞(NIO): 代码行为模式为一个请求一个线程。每一个连接通道都会注册到多路复用器上,多路复用器会轮询所有连接通道,发现有IO请求时会启动一个线程进行处理。多路复用器需要再一个单独的线程中去执行轮询操作。
异步阻塞(NIO): 代码行为模式为当应用发起IO操作后,马上就会受到状态回复,当内核处理完IO操作后,通知应用程序IO最新状态。在OS内核层,系统会调用select系统调用对IO进行处理,select会同时监听多个文件句柄,提高了并发性,但是对于每个文件句柄的读写操作时阻塞的。
异步非阻塞(AIO):代码行为模式为应用进程发起一个IO操作后立即返回,等内核处理完IO操作后,会通知应用进程IO数据,应用进程取得数据进行处理即可。
如果理解不太清楚,可以通过下面例子进行辅助理解:
同步阻塞: 小明去书店买书,然后一直在柜台前等着,并且一直询问:有书吗?有书吗?……
同步非阻塞: 小明去书店买书,跟柜台说完后就去网吧玩了,玩一会就跑回柜台问一下:书准备好了吗?
异步阻塞:小明给书店打电话说要定新书,当在网吧玩时,书店打来电话,说有新书了,小明自己去拿书。
异步非阻塞:小明给书店打电话说定新书,当在网吧玩时,书店打电话说,有新书到了,并派人将新书送过来。
应用场景
BIO:适用于方法简单,开发速度快。但是连接数较小,对服务器性能较高,并发性差。NIO:适用于连接数多且为短链接,轻数据量的服务器,比如通讯服务器,编程方式复杂,JDK1.4中引入。
AIO:适用于连接数少且为长连接,重数据量的服务器,比如视频服务器,充满在JDK1.7中引入,所以Android系统中,只用5.0+版本才支持AIO。
非阻塞IO复用
在操作系统层,处理非阻塞IO复用时,一般会有三种方式:select、poll、epoll。select:通过设置或存放文件句柄标志位数据结构来处理IO,有如下缺点:
单线程可以监控的fd数量有限,默认为2048个,修改需要改动内核常量设置。
根据fd数量的大小决定,用户态和内核态之间传输效率
轮询起扫描时占用大量CPU计算时间
多线程同步机制的处理
poll:与select一样,将用户态的数据复制到内核中,然后遍历所有句柄,如果有就绪IO则将此IO放入等待队列中,如果没有准备就绪的IO,则挂起轮询进程,当IO被唤醒时再次启动轮询进程遍历fd表。
在poll中,fd使用链表存储,所以遍历的fd数量没有限制
用户态和内核态传输效率依然取决于fd数量
报告fd就绪后未被处理,下次poll时会再次报告该fd
epoll:epoll支持水平触发(Level-Triggered,支持阻塞和非阻塞模式)和边缘触发(Edge-Triggered,只有非阻塞模式)。
它只通知应用进程那些fd刚刚变为就绪状态,并且只会通知一次。
对于fd数据结构复制问题,epoll采用mmap共享内存来减少复制。
epoll通过向内核注册fd的方式来异步接收IO状态变化,
连接数受内存大小影响,1G内存可打开10万左右的fd
性能比较
从以上介绍可以看出,select和poll都是采用轮询的方式线性扫描,所以扫描速度会根据fd数量而改变。epoll采用内核回调机制,只有活跃的IO才会被内核调用epoll_wait。所以在连接数较大但活跃度较小的情况下,不会出现性能的下降,但是当活跃性较高时,采用poll或select方式会更好。Naga Socket 库
Naga是个简单的Java Socket NIO库,使用线程队列和selector等技术,实现了类似java JDK1.7中的AIO的功能、封装简单,占用资源小、使用方便,可在Android平台中完美运行。GitHub连接地址为:https://github.com/techery/NagaNage 运行构架图
架构介绍
Naga 在 IO激发方面使用线程队列技术,图中橙色区域为Task Queue,队列中的每个channelResponder都集成了Runable接口,其中还包括:channel实体:包括建立当前任务时的地址、端口号。
channel状态处理接口:包括readable、writeable、connected、accepted等
socket读写操作类:SocketWriter、SocketReader
Packet队列:包括当前任务的收发Packet,Selcetor遍历此包队列的信息,进行处理。
架构流程
在主线程中创建NIOServer,NIOServer包括Selector、Task Queue等运行环境上下文。利用NIOServer的openSocket建立连接,并加入channelResponder到任务队列中。
channelResponder的建立过程中,会包括host address、port、channel、channel在selector重点的注册、packet queue以及相关接口,封装在实现Runable接口的类中。
NIOServer会在主线程中开启轮询操作。首先轮询是否在Task Queue中存在任务元组,若存在,唤醒selector遍历其中的Packet Queue对数据进行IO操作,直到Packet Queue为空后,将selector挂起。
Naga Socket Client 代码
SocketStateCallBack connectSocktListener; mNioService = new NIOService(); if(mNioSocket == null) { mNioSocket = mNioService.openSocket( ShareConstants.SERVICR_HOST, ShareConstants.SERVICR_PORT); breakFlag = true; mNioSocket.listen(new SocketObserver() { @Override public void connectionOpened(NIOSocket nioSocket) { breakFlag = true; DugUtils.logI(getClass(), "connected"); connectSocktListener.connectSocketSuccessful(nioSocket); } @Override public void packetSent(NIOSocket socket, Object tag) { } @Override public void packetReceived(NIOSocket socket, byte[] packet) { String strReceive = ""; try { strReceive = new String(packet, "UTF-8"); String[] strSplit = strReceive.split(mStrSplit); for (int i = 0; i < strSplit.length; i++) { if (JsonUtils.isGoodJson(strSplit[i])) { connectSocktListener.receiveRespMessage(strSplit[i]); } } DugUtils.logI(getClass(), "Reply message: " + strReceive); } catch (UnsupportedEncodingException e) { e.printStackTrace(); DugUtils.logI(getClass(), "Reply error message: " + strReceive); } } @Override public void connectionBroken(NIOSocket nioSocket, Exception exception) { breakFlag = false; if (mContext != null) { if (!nioSocket.isOpen()) { DugUtils.logI(getClass(), "the connection is broken."); isNioSocketSuccessful = false; connectSocktListener.connectionIsBroken(); } } mNioSocket = null; } while(breakFlag){ mNioService.selectBlocking(); }
Naga Socket Server 代码
NIOService service = new NIOService(); NIOServerSocket socket = service.openServerSocket(port); socket.listen(new ServerSocketObserverAdapter() { public void newConnection(NIOSocket nioSocket) { System.out.println("Client " + nioSocket.getIp() + " connected."); nioSocket.listen(new SocketObserverAdapter() { public void packetReceived(NIOSocket socket, byte[] pac ket) { socket.write(packet); } public void connectionBroken(NIOSocket nioSocket, Exception exception) { System.out.println("Client " + nioSocket.getIp() + " disconnected."); } }); } }); socket.setConnectionAcceptor(ConnectionAcceptor.ALLOW); while (true) { service.selectBlocking(); }
小结
本文章介绍了Socket的基本概念和种类,并对Android客户端中可以使用的Nage AIO Socket 库进行了分析,由于时间有限,如读者发现存在错误,请留言交流。下一篇,要对Naga库中的Socket+SSL功能进行介绍,实现Socket的加密通信功能。
相关文章推荐
- 时间选择器DatePickerDialog的简单demo
- Android studio ButterKnife插件
- Android 去掉ScrollView、GridView、ListView向上 滑动时顶部的投影/阴影
- Android 超强动态环信比列图 (自定义View)
- phonegap-我的第一个android应用
- Android ListView使用BaseAdapter与ListView的优化
- GitHub 优秀的 Android 开源项目
- Android Context 是什么?
- android之intent显式,显式学习
- android gridview画分割线
- Android过渡动画之共享元素实现以及遇到的一些问题
- Android各种效果集合
- Android 去标题栏,Activity 满屏那些你所不知道的 Theme Style
- Android 检查内存溢出
- 解决:AndroidStudio导入第三方工程出现Error:Attribute application@icon value=(@mipmap/ic_launcher)
- [Android] EditText 的 inputType属性
- Universal-Image-Loader 使用
- 一路走来 Android NDK 踩过的坑
- android中常见的Drawable资源有哪些?
- AndroidAnnotations简单示例