/**
* 非阻塞IO多线线程服务端
* 当一个任务进入多线程,这个任务线程需要处理接收信息、发送信息、因而发生I/O阻塞问题
* 利用selector可以实现异步
*
*/
public class EchoServer02 {
//轮询器,处理IO阻塞问题
private Selector selector = null;
private ServerSocketChannel serverSocketChannel = null;
private int port = ;
private Charset charset = Charset.forName("GBK");//编码方式 public EchoServer02() throws IOException{
//创建一个Selector对象
selector = Selector.open();
//这个方法没有与任何本地端口绑定,并且处于阻塞模式;
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().setReuseAddress(true);
//使ServerSocketChannel工作于非阻塞模式
serverSocketChannel.configureBlocking(false);// IO异步处理
//把服务器与本地端口绑定
serverSocketChannel.socket().bind(new InetSocketAddress(port));//绑定服务器端口
System.out.println("服务器已启动");
} public void service() throws Exception{
/*SeverSocketChannel或Socket类通过register()方法向Selector注册事件时,
register()方法会创建一个SelectionKey对象,
这个SelectionKey对象是用来跟踪注册事件的句柄。
在SelectionKey对象的有效时间,Selector会一直监控与SelectionKey对象相关的事件,
如果事件发生,就会把SelectionKey对象加入seleected-keys集合中。*/
//将ServerSocketChannel注册到Selector上
//只要ServerSocketChannel及SocketChannel向Selector注册了特定的事件,Selector就会监控这些事件是否发送
//SelecitonKey.OP_ACCEPT:接收连接就绪事件,表示服务器监听到了客户连接,服务器可以接收这个链接了。常量值为16
//这个客户SocketChannel会被Selector监控到
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//获取Selector中的SelectionKey数量
while(selector.select() > ){//第一层循环
//相关事件已经被Selector捕获的SelectionKey的集合。
Set readKeys = selector.selectedKeys();
Iterator iterator = readKeys.iterator();
while(iterator.hasNext()){//第二层循环
SelectionKey key = null;
try{//处理SelectionKey
key = (SelectionKey)iterator.next();//取出一个SelectionKey
//把SelectionKey从Selector的selected-集合中删除
iterator.remove();
if(key.isAcceptable()){//处理连接就绪事件
//获得与SelectionKey关联的ServerSocketChannel
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
//获得与客户连接的SocketChannel,这个SocketChannel默认情况是阻塞的
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("接收到客户的连接,来自:"+socketChannel.socket().getInetAddress()
+":"+socketChannel.socket().getPort());
//把SocketChannel设置为非阻塞模式,
socketChannel.configureBlocking(false);
//创建一个用于存放用户发送来的数据的缓冲区
ByteBuffer buffer = ByteBuffer.allocate();
//把SocketChannel向Selector注册就读事件和就绪事件,且关联了一个buffer附件
socketChannel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE,buffer);
}
if(key.isReadable()){//处理读就绪事件
receive(key);
}
if(key.isWritable()){//处理写就绪事件
send(key);
}
}catch (Exception e) {
e.printStackTrace();
try {
if(key != null){
//使这个Seleciton失效
//使得Selector不再监控这个SelectionKey感兴趣的事件
key.cancel();
key.channel().close();//关闭这个SelectionKey关联的SocketChannel
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
} public void send(SelectionKey key) throws IOException{
//获得与SelectionKey关联的ByteBuffer
ByteBuffer buffer = (ByteBuffer) key.attachment();
//获得与SelectionKey关联的SocketChannel
SocketChannel socketChannel = (SocketChannel) key.channel();
//把极限设为位置,把位置设为0
buffer.flip();
//按照GBK编码,把buffer中的字节转换为字符串
String data = decode(buffer);
//如果还没有读到一行数据就返回
if(data.indexOf("\r\n") == -){
return ;
}
//截取一行数据
String outputData = data.substring(, data.indexOf("\n")+);
System.out.println(outputData);
//把输出的字符串安装GBK编码,转换为字节,把它放入outputBuffer
ByteBuffer outputBuffer = encode("ehco:" + outputData);
//输出outputBuffer中所有的字节
while(outputBuffer.hasRemaining()){
socketChannel.write(outputBuffer);
//把outputData字符串按照GBK编码,转换为字节 ,把它放入ByteBuffer中
ByteBuffer temp = encode(outputData);
//把buffer的位置设为temp的极限
buffer.position(temp.limit());
//删除buffer中已经处理的数据
buffer.compact();
//如果已经输出了字符串"bye\r\n",就使SelectionKey失效,并关闭SocketChannel
if(outputData.equals("bye\r\n")){
key.cancel();
socketChannel.close();
System.out.println("关闭与客户的连接");
}
}
}
/**
* receive()方法把读入的数据都放在一个ByteBuffer中,
* send()方法就从这个ByteBuffer中取出数据
* 如果ByteBuffer中还没有一行字符串,就什么不做,直接退出send()方法
* @param key
* @throws IOException
*/
public void receive(SelectionKey key) throws IOException{
//获得与SelectionKey关联的附件
ByteBuffer buffer = (ByteBuffer) key.attachment();
//获得与SelectionKey关联的SocketChannel
SocketChannel socketChannel = (SocketChannel) key.channel();
//创建一个ByteBuffer,用于存放读到的数据
ByteBuffer readBuff = ByteBuffer.allocate();
socketChannel.read(readBuff);
readBuff.flip();
//把buffer的极限设为容量
buffer.limit(buffer.capacity());
//把readBuff中内容拷贝到buffer中,
//假设buffer容量足够大,不会出现缓冲区溢出异常
buffer.put(readBuff);
} public String decode(ByteBuffer buffer){//解码,将字节转换为字符串的过程
CharBuffer charBuffer = charset.decode(buffer);
return charBuffer.toString();
} public ByteBuffer encode(String str){//编码,将字符串转换为字节
return charset.encode(str);
} public static void main(String[] args) throws Exception{
EchoServer02 server02 = new EchoServer02();
server02.service();
} }

java 服务端I/O非阻塞实现05的更多相关文章

  1. Java 服务端监控方案(四. Java 篇)

    http://jerrypeng.me/2014/08/08/server-side-java-monitoring-java/ 这个漫长的系列文章今天要迎来最后一篇了,也是真正与 Java 有关的部 ...

  2. 那些年,我们见过的 Java 服务端“问题”

    导读 明代著名的心学集大成者王阳明先生在<传习录>中有云: 道无精粗,人之所见有精粗.如这一间房,人初进来,只见一个大规模如此.处久,便柱壁之类,一一看得明白.再久,如柱上有些文藻,细细都 ...

  3. 俯瞰 Java 服务端开发

    原文首发于 github ,欢迎 star . Java 服务端开发是一个非常宽广的领域,要概括其全貌,即使是几本书也讲不完,该文将会提到许多的技术及工具,但不会深入去讲解,旨在以一个俯瞰的视角去探寻 ...

  4. Java服务端性能优化

    <Java程序性能优化>说性能优化包含五个层次:设计调优.代码调优.JVM调优.数据库调优.操作系统调优. 常用的几个代码优化方案: 使用单例 对于IO处理.数据库连接.配置文件解析加载等 ...

  5. java服务端集成极光消息推送--详细开发步骤

    1.极光推送账号准备 要使用极光消息推送必须先在官方网站上注册账号,并添加应用. 产品介绍:https://docs.jiguang.cn/jpush/guideline/intro/ 注册开发者账号 ...

  6. RPC学习--C#使用Thrift简介,C#客户端和Java服务端相互交互

    本文主要介绍两部分内容: C#中使用Thrift简介 用Java创建一个服务端,用C#创建一个客户端通过thrift与其交互. 用纯C#实现Client和Server C#服务端,Java客户端 其中 ...

  7. ajax跨域请求,页面和java服务端的写法

    方法一(jsonp): 页面ajax请求的写法: $.ajax({ type : "get", async : false, cache : false, url : " ...

  8. Flex通信-Java服务端通信实例

    转自:http://blessht.iteye.com/blog/1132934Flex与Java通信的方式有很多种,比较常用的有以下方式: WebService:一种跨语言的在线服务,只要用特定语言 ...

  9. “快的打车”创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - V2EX

    "快的打车"创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - ...

随机推荐

  1. springboot+mybatis+dubbo+aop日志第一篇

    本篇文章主要讲述项目搭建过程,不会涉及过多的基础知识,本项目是作者对前段时间学习的一个总结,主要使用到技术有:maven父子工程.springboot.mybatis.dubbo.zookeeper. ...

  2. 设计模式总结篇系列:装饰器模式(Decorator)

    在面向对象设计过程中,经常会遇到需要对现有的类的功能进行扩展,通常我们可以采用继承的方式.例如老罗最近在做手机,一开始需要定义手机所应具有的功能: interface Phone{ public vo ...

  3. javascript入门篇(一)

    未定义:undefined 布尔类型值:true,false 判断类型:typeof() 绝对值:Math.abs(-1) 声明常量:const   声明变量:var 小数两值互换如果出现问题,可以通 ...

  4. Zabbix3.0基础教程之一:系统监控,zabbix安装与基本配置

    一.安装环境: 系统环境: 系统版本:CentOS Linux release 7.4.1708 (Core) 内核版本:3.10.0-693.el7.x86_64 关闭Firewall与SELinu ...

  5. 脚本检测Kafka和Zookeeper

    Java测试环境中Kafka最近总是自动停止,所有写了一个简单的脚本来监听Kafka和Zookeeper,其中Kafka监听端口为9092,Zookeeper监听端口为2181,脚本如下: #!/bi ...

  6. 内核中 xxx_initcall 的调用过程分析

    内核版本:linux-4.19 上一篇文章提到了这段代码: arch_initcall_sync(of_platform_default_populate_init); 它的功能是完成 device_ ...

  7. 一统江湖的大前端(7)React.js-从开发者到工程师

    目录 一. 前端打怪升级指南 1.1 我应该从哪个框架开始学? 1.2 一次转职 1.3 二次转职 1.4 转职-其他 二. 为什么你应该学习React 2.1 技术栈的延伸 2.2 组件化开发 2. ...

  8. VisualStudio移动开发(C#、VB.NET)Smobiler开发平台——BarcodeView控件的使用方式,.Net移动开发

    BarcodeView控件 一.          样式一 我们要实现上图中的效果,需要如下的操作: 从工具栏上的“Smobiler Components”拖动一个BarcodeView控件到窗体界面 ...

  9. MVC防止CSRF攻击

    可能我们大多数人做web的时候不会太注意这个问题,但是这是一个很重要的一个点.我们写代码写业务的时候也应该从各方面多思考. 首先就是先简单介绍下什么是CSRF CSRF 全程是 Cross-site ...

  10. 【c#】队列(Queue)和MSMQ(消息队列)的基础使用

    首先我们知道队列是先进先出的机制,所以在处理并发是个不错的选择.然后就写两个队列的简单应用. Queue 命名空间 命名空间:System.Collections,不在这里做过多的理论解释,这个东西非 ...