DatagramChannel和SocketChannel都实现定义读写功能,ServerSocketChannel不实现,只负责监听传入的连接,并建立新的SocketChannel,本身不传输数据。

Socket通道被实例化时都会创建一个对等的socket,通过此方式创建的socket都会有关联的通道,通过getChannel()获取。

继承于 SelectableChannel,所以socket可以在非阻塞模式下运行:

Readiness Selection:就绪选择,查询通道的机制,该机制可以判断通道是否准备好执行下一个目标操作(读,写...),其价值在于潜在的大量通道可以同时进行就绪检查,真正的就绪选择需要由操作系统来做,处理IO请求,并通知各个线程数据准备情况。

Selector选择器:提供了这种抽象(抽象接口),是的Java代码能够以可移植的方式,请求底层操作系统提供这种服务。

Selector选择器类:管理着一个被注册的通道集合的信息和他们的状态,通道和选择器是一起被注册的,并且使用选择器来更新通道状态。

一个通道可以被注册到多个选择器上,但在每个选择器上,只能注册一次。

SelectionKey选择键:封装了通道和选择器的注册关系,选择键被SelectableChannel.register()返回并提供标识这种注册关系的标记。

通道在被注册到选择器之前必须设置为noblocking模式,正常状态。

chanel.register(selector, keystate):通道注册选择器。

selector.select():阻塞操作,直到某一个channel的keystate发生。

selectionKey.cancel(),取消注册关系。

通道关闭,相关的注册键会自动取消,选择器关闭,则所有注册到该选择器的通道都将被注销,并且相关的键会立刻失效。

selectionkey包含两个以整数型式进行编码的比特掩码,一个用于指示那些通道和选择器组合所关心的操作,另一个表示通道准备好要执行的操作。当前的interest集合可以通过调用见对象的interestOps()方法来获取,且永远不会被选择器改变,但可以调用interestOps()方法,传入一个新的比特码来改变。

readyOpts()获取相关通道的已就绪的操作,ready集合是interest集合的子集,表示从上次调用select()之后已经就绪的操作。如下:

if((key.readOps() & SelctionKey.OP_READ) != 0){

buffer.clear();

key.channel().read(buffer);

do()....

}

附加参数:attach()

SelectionKey key = SelectableChannel.register(Selector, SelectionKey.OP_XXX, paramObj);

等价:

SelectionKey key = SelectableChannel.register(Selector, SelectionKey.OP_XXX);

key.attach(paramObj);

SelectionKey 多线程应用同步问题。

选择器:

Selector上的已注册键集合中,会存在失效键、null,keys()返回,不可修改。

已选择键集合,selectedKeys()返回,已经准备好的键集合,可能为空。

核心:选择过程,是对select(),poll(),epoll()等本地调用(native call)或者类似的操作系统的本地调用的包装(抽象),期间,将执行以下过程:

  1. 已取消的键的集合将会被检查,如果非空,则会被从其它两个集合(已注册、已选择)移除,相关通道将会被注销,键被清空。

  2. 已注册键的集合的键的interest集合将会被检查,就绪条件确定,底层操作系统对通道所关心的操作的就绪状态进行检查,如果没有,则阻塞当前(超时值)。

    对于已经就绪的通道执行:

    a. 如果通道的键未在已选择的键集合中,那么键的reay集合将会被清空,然后设置当前准备好的比特掩码。

    b. 如果通道的键已在已选择的键集合中,键的ready集合更新。不再就绪的状态不会被清除。

  3. select返回的是从上一次select()调用之后进入就绪状态的通道数量,之前的调用中已经就绪的,并且本次调用中仍然就绪的不会被计入。

使用内部已取消的键的集合来延迟注销,防止线程在取消键时阻塞及与正在进行的选择操作冲突的优化,

三种形式的select: select(), select(timeout),selectNow()(非阻塞,立刻返回当前状况)。

调用 Selector 对象的 wakeup( )方法将使得选择器上的第一个还没有返回的选择操作立即回。如果当前没有在进行中的选择,那么下一次对 select( )方法的一种形式的调用将立即返回。后续的选择操作将正常进行。在选择操作之间多次调用 wakeup( )方法与调用它一次没有什么不同。有时这种延迟的唤醒行为并不是您想要的。您可能只想唤醒一个睡眠中的线程,而使得后续的

选择继续正常地进行。您可以通过在调用 wakeup( )方法后调用 selectNow( )方法来绕过这个问题。

通常的做法是在选择器上调用一次 select 操作(这将更新已选择的键的集合),然后遍历 selectKeys( )方法返回的键的集合。在按顺序进行检查每个键的过程中,相关的通道也根据键的就绪集合进行处理。然后键将从已选择的键的集合中被移除(通过在 Iterator对象上调用 remove( )方法),然后检查下一个键。完成后,通过再次调用 select( )方法重复这个循环。如下:

package org.windwant.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* Created by windwant on 2016/10/27.
*/
public class SocketChannelOpt { private static final String HOST = "localhost";
private static final int PORT = 8888; private static ExecutorService read = Executors.newFixedThreadPool(5);
private static ExecutorService write = Executors.newFixedThreadPool(5); public static void main(String[] args){
ServerSocketChannel serverSocketChannel = null;
ServerSocket serverSocket = null;
Selector selector = null;
try {
serverSocketChannel = ServerSocketChannel.open();//工厂方法创建ServerSocketChannel
serverSocket = serverSocketChannel.socket(); //获取channel对应的ServerSocket
serverSocket.bind(new InetSocketAddress(HOST, PORT)); //绑定地址
serverSocketChannel.configureBlocking(false); //设置ServerSocketChannel非阻塞模式
selector = Selector.open();//工厂方法创建Selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//通道注册选择器,接受连接就绪状态。
while (true){//循环检查
if(selector.select() == 0){//阻塞检查,当有就绪状态发生,返回键集合
continue;
} Iterator<SelectionKey> it = selector.selectedKeys().iterator(); //获取就绪键遍历对象。
while (it.hasNext()){
SelectionKey selectionKey = it.next();
//处理就绪状态
if (selectionKey.isAcceptable()){
ServerSocketChannel schannel = (ServerSocketChannel) selectionKey.channel();//只负责监听,阻塞,管理,不发送、接收数据
SocketChannel socketChannel = schannel.accept();//就绪后的操作,刚到达的socket句柄
if(null == socketChannel){
continue;
}
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ); //告知选择器关心的通道,准备好读数据
}else if(selectionKey.isReadable()){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(4*1024); StringBuilder result = new StringBuilder();
while (socketChannel.read(byteBuffer) > 0){//确保读完
byteBuffer.flip();
result.append(new String(byteBuffer.array()));
byteBuffer.clear();//每次清空 对应上面flip()
} System.out.println("server receive: " + result.toString());
socketChannel.register(selector, SelectionKey.OP_WRITE); }else if(selectionKey.isWritable()){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
String sendStr = "server send data: " + Math.random();
ByteBuffer send = ByteBuffer.wrap(sendStr.getBytes());
while (send.hasRemaining()){
socketChannel.write(send);
}
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println(sendStr);
}
it.remove();
}
} } catch (IOException e) {
e.printStackTrace();
}
}
}

Selector多线程执行,同步需求。

一个线程监控通道的就绪状态,一个线程池处理业务需求。线程池也可以扩展为不同的业务处理线程池,如日志、业务、心跳。

package org.windwant.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 线程处理读取,写出
* Created by windwant on 2016/10/27.
*/
public class TSocketChannelOpt { private static final String HOST = "localhost";
private static final int PORT = 8888; private static ExecutorService read = Executors.newFixedThreadPool(5);
private static ExecutorService write = Executors.newFixedThreadPool(5); public static void main(String[] args){
ServerSocketChannel serverSocketChannel = null;
ServerSocket serverSocket = null;
Selector selector = null;
try {
serverSocketChannel = ServerSocketChannel.open();//工厂方法创建ServerSocketChannel
serverSocket = serverSocketChannel.socket(); //获取channel对应的ServerSocket
serverSocket.bind(new InetSocketAddress(HOST, PORT)); //绑定地址
serverSocketChannel.configureBlocking(false); //设置ServerSocketChannel非阻塞模式
selector = Selector.open();//工厂方法创建Selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//通道注册选择器,接受连接就绪状态。
while (true){//循环检查
if(selector.select() == 0){//阻塞检查,当有就绪状态发生,返回键集合
continue;
} Iterator<SelectionKey> it = selector.selectedKeys().iterator(); //获取就绪键遍历对象。
while (it.hasNext()){
SelectionKey selectionKey = it.next();
it.remove();
//处理就绪状态
if (selectionKey.isAcceptable()){
ServerSocketChannel schannel = (ServerSocketChannel) selectionKey.channel();//只负责监听,阻塞,管理,不发送、接收数据
SocketChannel socketChannel = schannel.accept();//就绪后的操作,刚到达的socket句柄
if(null == socketChannel){
continue;
}
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ); //告知选择器关心的通道,准备好读数据
}else if(selectionKey.isReadable()){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
read.execute(new MyReadRunnable(socketChannel)); // SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// ByteBuffer byteBuffer = ByteBuffer.allocate(4*1024);
//
// StringBuilder result = new StringBuilder();
// while (socketChannel.read(byteBuffer) > 0){//确保读完
// byteBuffer.flip();
// result.append(new String(byteBuffer.array()));
// byteBuffer.clear();//每次清空 对应上面flip()
// }
//
// System.out.println("server receive: " + result.toString());
socketChannel.register(selector, SelectionKey.OP_WRITE); }else if(selectionKey.isWritable()){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
write.execute(new MyWriteRunnable(socketChannel));
// String sendStr = "server send data: " + Math.random();
// ByteBuffer send = ByteBuffer.wrap(sendStr.getBytes());
// while (send.hasRemaining()){
// socketChannel.write(send);
// }
// System.out.println(sendStr);
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
} } catch (IOException e) {
e.printStackTrace();
}
} static class MyReadRunnable implements Runnable { private SocketChannel channel; public MyReadRunnable(SocketChannel channel){
this.channel = channel;
} @Override
public synchronized void run() {
ByteBuffer byteBuffer = ByteBuffer.allocate(4*1024); StringBuilder result = new StringBuilder();
try {
while (channel.read(byteBuffer) > 0){//确保读完
byteBuffer.flip();
result.append(new String(byteBuffer.array()));
byteBuffer.clear();//每次清空 对应上面flip()
}
System.out.println("server receive: " + result.toString());
} catch (IOException e) {
e.printStackTrace();
} }
} static class MyWriteRunnable implements Runnable { private SocketChannel channel; public MyWriteRunnable(SocketChannel channel){
this.channel = channel;
} @Override
public void run() {
String sendStr = "server send data: " + Math.random();
ByteBuffer send = ByteBuffer.wrap(sendStr.getBytes());
try {
while (send.hasRemaining()) {
channel.write(send);
}
System.out.println(sendStr);
}catch (Exception e){
e.printStackTrace();
} }
}
}
 

JAVA NIO Socket通道的更多相关文章

  1. Java NIO Channel通道

    原文链接:http://tutorials.jenkov.com/java-nio/channels.html Java NIO Channel通道和流非常相似,主要有以下几点区别: 通道可以读也可以 ...

  2. Java nio socket与as3 socket(粘包解码)连接的应用实例

    对Java nio socket与as3 socket连接的简单应用 <ignore_js_op>Java nio socket与as3 socket连接的应用实例.rar (9.61 K ...

  3. Java NIO Socket 非阻塞通信

    相对于非阻塞通信的复杂性,通常客户端并不需要使用非阻塞通信以提高性能,故这里只有服务端使用非阻塞通信方式实现 客户端: package com.test.client; import java.io. ...

  4. Java NIO之通道

    一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...

  5. 【NIO】Java NIO之通道

    一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...

  6. Java NIO Socket编程实例

    各I/O模型优缺点 BIO通信模型 BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能处理一个客户端连接 线程池I/O编程 假如所有可用 ...

  7. 【Java TCP/IP Socket】Java NIO Socket VS 标准IO Socket

    简介 Java  NIO从JDK1.4引入,它提供了与标准IO完全不同的工作方式. NIO包(java.nio.*)引入了四个关键的抽象数据类型,它们共同解决传统的I/O类中的一些问题.    1. ...

  8. java nio socket使用示例

    这个示例,实现一个简单的C/S,客户端向服务器端发送消息,服务器将收到的消息打印到控制台,并将该消息返回给客户端,客户端再打印到控制台.现实的应用中需要定义发送数据使用的协议,以帮助服务器解析消息.本 ...

  9. Java NIO:通道

    最近打算把Java网络编程相关的知识深入一下(IO.NIO.Socket编程.Netty) Java NIO主要需要理解缓冲区.通道.选择器三个核心概念,作为对Java I/O的补充, 以提升大批量数 ...

随机推荐

  1. Entity Framework 6 Recipes 2nd Edition(10-7)译 -> TPH继承模型中使用存储过程

    10-7. TPH继承模型中使用存储过程 问题 用一个存储过程来填充TPH继承模型的实体 解决方案 假设已有如Figure 10-7所示模型. 我们有两个派生实体: Instructor(教员)和St ...

  2. Gulp探究折腾之路(I)

    前言: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成:使用她,我们不仅可以很愉快的编写代码 ...

  3. FASTDFS调研报告(V1.0)

    之前的文章,现在放出来,以供参阅. 一.fastdfs简介 FastDFS是一个轻量级的开源分布式文件系统 FastDFS主要解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡 Fas ...

  4. 非阻塞/异步(epoll) openssl

    前段时间在自己的异步网络框架handy中添加openssl的支持,当时在网络上搜索了半天也没有找到很好的例子,后来自己慢慢的摸索,耗费不少时间,终于搞定.因此把相关的资料整理一下,并给出简单的例子,让 ...

  5. 前端学HTTP之WEB服务器

    前面的话 Web服务器每天会分发出数以亿计的Web页面,它是万维网的骨干.本文主要介绍WEB服务器的相关内容 总括 Web服务器会对HTTP请求进行处理并提供响应.术语“Web服务器”可以用来表示We ...

  6. ASP.NET 5 - $.ajax post JSON.stringify(para) is null

    JavaScript 代码: var para = {}; para.id = $("#ad-text-id").val(); para.title = $("#ad-t ...

  7. [Keras] Develop Neural Network With Keras Step-By-Step

    简单地训练一个四层全连接网络. Ref: http://machinelearningmastery.com/tutorial-first-neural-network-python-keras/ 1 ...

  8. 【Python五篇慢慢弹(3)】函数修行知python

    函数修行知python 作者:白宁超 2016年10月9日21:51:52 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给出的pythondoc ...

  9. 用SignalR 2.0开发客服系统[系列3:实现点对点通讯]

    前言 交流群:195866844 目录: 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 用SignalR 2.0开发客服系统[系列2:实现聊天室] 真的很感谢大家的支持,今天发表系列3 ...

  10. Django admin美化插件suit应用[原创]

    前言 由于比较懒,自己弄了一个用户验证,没有自己写后台,用了django自带的user认证,并通过admin直接进行管理,但默认的admin并不漂亮,于是使用了这个django-suit插件,效果对比 ...