您的位置:首页 > 移动开发 > Android开发

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调研了一下:

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/Naga

Nage 运行构架图



架构介绍

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的加密通信功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: