jdk提供的NIO使用:

概览:https://blog.csdn.net/the_fool_/article/details/83000648

博主抄写了网上的demo,略作修改与调整,原文链接:

Demo01:抄写地址忘记了。。。

1、服务端代码:

package com.test3;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator; /**
* NIO服务端代码
* @author http://
* @author ZX
* 监听客户端连接,接收、发送消息
*
*/
public class AServer {
public static void main(String[]args)throws Exception{
System.out.println("=================");
//创建选择器
Selector selector = Selector.open();
//创建打开服务端的监听
ServerSocketChannel sChannel = ServerSocketChannel.open();
//绑定本地地址
sChannel.socket().bind(new InetSocketAddress(9999));
//设置非阻塞模式
sChannel.configureBlocking(false);
//将通道绑定到选择器,非阻塞通道才能注册到选择器,第二个参数好像是方式或者操作吧
sChannel.register(selector, SelectionKey.OP_ACCEPT); TcpProtocal protocol = new TcpProtocal();
//循环监听等待
while (true){
//
if(selector.select(3000)==0){
System.out.println("继续等待");
continue;
}
//间听到的可操作集合
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while (keyIter.hasNext()){
SelectionKey key = keyIter.next();
//这是干嘛的?获取下下个?一点用没有啊???
/* SelectionKey key1;
if(keyIter.hasNext()){
key1 = keyIter.next();
}*/
try {
//如果有客户端连接请求
if(key.isAcceptable()){
protocol.handleAccept(key);
}
//如果有数据发送
if(key.isReadable()){
protocol.handleRead(key);
protocol.handleWrite(key);
}
//是否有效,是否可发送给客户端
if(key.isValid()&&key.isWritable()){
//protocol.handleWriteMsg(key,"我服务端在这里说点事情");
}
}catch (IOException e){
keyIter.remove();
e.printStackTrace();
continue;
}
//删除处理过的键
keyIter.remove(); } } }
}

2、协议:

package com.test3;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset; /**
* 定义一个协议传输信息
*/
public class TcpProtocal {
private int bufferSize=1024;
/** 接受一个SocketChannel处理*/
public void handleAccept(SelectionKey key) throws Exception{
//返回创建此键的通道,接收客户端建立连接的请求,并返回socketChannel对象
SocketChannel clientChannel=((ServerSocketChannel)key.channel()).accept();
//设置非阻塞
clientChannel.configureBlocking(false);
//注册到selector
clientChannel.register(key.selector(),SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize)); }
/** 从一个一个SocketChannel读取信息*/
public void handleRead(SelectionKey key) throws Exception{
//获得与客户端通信的通道
SocketChannel clientChannel=(SocketChannel)key.channel();
//得到并清空缓冲区并清空缓冲区
ByteBuffer buffer= (ByteBuffer)key.attachment();
buffer.clear();
//读取信息获得的字节数
int byteRead = clientChannel.read(buffer);
if(byteRead==-1){
clientChannel.close();
}else{
//将缓冲区准备为数据传状态
buffer.flip();
//将字节转换为UTF-16
String receivedStr = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();
System.out.println("接收到来自"+clientChannel.socket().getRemoteSocketAddress()+"信息"+receivedStr);
key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
/*String sendStr ="已收到信息";
buffer=ByteBuffer.wrap(sendStr.getBytes("UTF-8"));
clientChannel.write(buffer);
//设置为下依稀读取写入做准备
key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);*/
} }
/** 向一个一个SocketChannel写入信息*/
public void handleWrite(SelectionKey key) throws Exception{
handleWriteMsg(key,null); } /** 向一个一个SocketChannel写入信息*/
public void handleWriteMsg(SelectionKey key,String msg) throws Exception{
if(msg==null||"".equals(msg)){
msg="服务器主动说:已收到建立请求消息";
}
//获得与客户端通信的通道
SocketChannel clientChannel=(SocketChannel)key.channel();
//得到并清空缓冲区并清空缓冲区
ByteBuffer buffer= (ByteBuffer)key.attachment();
buffer.clear();
String sendStr =msg;
buffer=ByteBuffer.wrap(sendStr.getBytes("UTF-8"));
clientChannel.write(buffer);
//设置为下依稀读取写入做准备
key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE); }
}

3、客户端处理类:

package com.test3;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset; public class TcpClientReadThread implements Runnable{
private Selector selector; public TcpClientReadThread(Selector selector){
this.selector=selector;
new Thread(this).start();
} public void run() {
try {
//select()方法只能使用一次,用过之后就会删除,每个连接到服务器的选择器都是独立的
while (selector.select()>0){
//遍历所有可以IO操作做的Channel对应的selectionKey
for(SelectionKey sk:selector.selectedKeys()){
//如果数据可读
if(sk.isReadable()){
//使用NIO读取Channel中可读数据
//获取通道信息
SocketChannel sc = (SocketChannel)sk.channel();
//创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取数据到缓冲区
sc.read(buffer);
//吊用此方法为读取写入做准备
buffer.flip(); String receiveStr = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();
System.out.println("收到服务器"+sc.socket().getRemoteSocketAddress()+"信息"+receiveStr);
//为下一次读取做准备
sk.interestOps(SelectionKey.OP_READ);
//删除正在处理的selectionKey
selector.selectedKeys().remove(sk); } }
}
}catch (Exception e){
e.printStackTrace();
}
}
}

4、客户端代码:

package com.test3;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Scanner; public class AClient { private Selector selector;
private SocketChannel socketChannel; private String hostIp;
private int hostPort; public AClient(String hostIp,int hostPort)throws Exception{
this.hostIp=hostIp;
this.hostPort=hostPort;
init(); }
public void init()throws Exception{
socketChannel=SocketChannel.open(new InetSocketAddress(hostIp,hostPort));
socketChannel.configureBlocking(false);
//打开并注册选择器信道、
selector= Selector.open();
socketChannel.register(selector,SelectionKey.OP_READ);
//启动读取线程
new TcpClientReadThread(selector); } /**
*发送字符串到服务器
*/
public void sendMsg(String msgg)throws Exception{
ByteBuffer writeBuffer = ByteBuffer.wrap(msgg.getBytes("UTF-8"));
socketChannel.write(writeBuffer);
}
static AClient aClient;
static boolean mFlag=true;
public static void main(String[]args)throws Exception{
aClient=new AClient("127.0.0.1",9999); new Thread(){
@Override
public void run() {
try {
aClient.sendMsg("客户端======");
while (mFlag){
Scanner sc = new Scanner(System.in);
String next = sc.next();
aClient.sendMsg(next);
}
}catch (Exception e){
mFlag=false;
e.printStackTrace();
}
}
}.start();
}
}

=================================================================================================

DEMO02:这个看起来更清楚一些,重新补充如下demo:

摘抄地址:

作者:anxpp
来源:CSDN
原文:https://blog.csdn.net/anxpp/article/details/51512200?utm_source=copy

Server:

package javanio;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.IOException;
import java.net.InetSocketAddress;
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.Set;
/**NIO 步骤*/ /**    打开ServerSocketChannel,监听客户端连接
    绑定监听端口,设置连接为非阻塞模式
    创建Reactor线程,创建多路复用器并启动线程
    将ServerSocketChannel注册到Reactor线程中的Selector上,监听ACCEPT事件
    Selector轮询准备就绪的key
    Selector监听到新的客户端接入,处理新的接入请求,完成TCP三次握手,简历物理链路
    设置客户端链路为非阻塞模式
    将新接入的客户端连接注册到Reactor线程的Selector上,监听读操作,读取客户端发送的网络消息
    异步读取客户端消息到缓冲区
    对Buffer编解码,处理半包消息,将解码成功的消息封装成Task
    将应答消息编码为Buffer,调用SocketChannel的write将消息异步发送给客户端
---------------------
作者:anxpp
来源:CSDN
原文:https://blog.csdn.net/anxpp/article/details/51512200?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!*/
public class Server {
private static int DEFAULT_PORT = 8081;
private static ServerHandle serverHandle;
public static void start(){
start(DEFAULT_PORT);
}
public static synchronized void start(int port){
if(serverHandle!=null){
serverHandle.stop();
} serverHandle = new ServerHandle(port);
new Thread(serverHandle,"Server").start();
}
public static void main(String[] args){
start();
} } class ServerHandle implements Runnable{
private Selector selector;
private ServerSocketChannel serverChannel;
private volatile boolean started; /**
* 构造方法
* @param port 指定要监听的端口号
*/
public ServerHandle(int port) {
try{
//创建选择器
selector = Selector.open();
//打开ServerSocketChannel,监听客户端连接
serverChannel = ServerSocketChannel.open();
//如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
serverChannel.configureBlocking(false);
//绑定端口 backlog设为1024
serverChannel.socket().bind(new InetSocketAddress(port),1024);
//监听客户端连接请求
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
//标记服务器已开启
started = true;
System.out.println("SERVER START,PORT:" + port);
}catch(IOException e){
e.printStackTrace();
System.exit(1);
}
}
public void stop(){
started = false;
} @Override
public void run() {
//循环遍历selector
while(started){
try{
//无论是否有读写事件发生,selector每隔1s被唤醒一次
selector.select(1000);
//阻塞,只有当至少一个注册的事件发生的时候才会继续.
// selector.select();
//返回已此通道已准备就绪的键集,已选择始终是键集的一个子集
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
SelectionKey key = null;
//处理所有的
while(it.hasNext()){
key = it.next();
it.remove();
try{
handleInput(key);
}catch(Exception e){
if(key != null){
key.cancel();
if(key.channel() != null){
key.channel().close();
}
}
}
}
}catch(Throwable t){
t.printStackTrace();
}
}
//selector关闭后会自动释放里面管理的资源
if(selector != null)
try{
selector.close();
}catch (Exception e) {
e.printStackTrace();
} } private void handleInput(SelectionKey key) throws IOException{
if(key.isValid()){
//处理新接入的请求消息
if(key.isAcceptable()){
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//通过ServerSocketChannel的accept创建SocketChannel实例
//完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
SocketChannel sc = ssc.accept();
//设置为非阻塞的
sc.configureBlocking(false);
//注册为读
sc.register(selector, SelectionKey.OP_READ);
}
//读消息
if(key.isReadable()){
SocketChannel sc = (SocketChannel) key.channel();
//创建ByteBuffer,并开辟一个1M的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取请求码流,返回读取到的字节数
int readBytes = sc.read(buffer);
//读取到字节,对字节进行编解码
if(readBytes>0){
//将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
buffer.flip();
//根据缓冲区可读字节数创建字节数组
byte[] bytes = new byte[buffer.remaining()];
//将缓冲区可读字节数组复制到新建的数组中
buffer.get(bytes);
String expression = new String(bytes,"UTF-8");
System.out.println("SERVER GET MSSG:" + expression);
//处理数据
String result = null;
try{
result = Calculator.cal(expression).toString();
}catch(Exception e){
result = "CAL ERR:" + e.getMessage();
}
//发送应答消息
doWrite(sc,result);
}
//没有读取到字节 忽略
// else if(readBytes==0);
//链路已经关闭,释放资源
else if(readBytes<0){
key.cancel();
sc.close();
}
}
}
}
//异步发送应答消息
private void doWrite(SocketChannel channel,String response) throws IOException{
//将消息编码为字节数组
byte[] bytes = response.getBytes();
//根据数组容量创建ByteBuffer
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
//将字节数组复制到缓冲区
writeBuffer.put(bytes);
//flip操作
writeBuffer.flip();
//发送缓冲区的字节数组
channel.write(writeBuffer);
//****此处不含处理“写半包”的代码
} void test(){ } }
class Calculator {
private final static ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
public static Object cal(String expression) throws ScriptException {
return jse.eval(expression);
}
}

Client:

package javanio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set; public class Client { private static String DEFAULT_HOST = "127.0.0.1";
private static int DEFAULT_PORT = 8081;
private static ClientHandle clientHandle;
public static void start(){
start(DEFAULT_HOST,DEFAULT_PORT);
}
public static synchronized void start(String ip,int port){
if(clientHandle!=null)
clientHandle.stop();
clientHandle = new ClientHandle(ip,port);
new Thread(clientHandle,"Server").start();
}
//向服务器发送消息
public static boolean sendMsg(String msg) throws Exception{
if(msg.equals("q")) return false;
clientHandle.sendMsg(msg);
return true;
}
public static void main(String[] args){
start();
} }
class ClientHandle implements Runnable{
private String host;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean started; public ClientHandle(String ip,int port) {
this.host = ip;
this.port = port;
try{
//创建选择器
selector = Selector.open();
//打开监听通道
socketChannel = SocketChannel.open();
//如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
socketChannel.configureBlocking(false);//开启非阻塞模式
started = true;
}catch(IOException e){
e.printStackTrace();
System.exit(1);
}
}
public void stop(){
started = false;
}
@Override
public void run() {
try{
doConnect();
}catch(IOException e){
e.printStackTrace();
System.exit(1);
}
//循环遍历selector
while(started){
try{
//无论是否有读写事件发生,selector每隔1s被唤醒一次
selector.select(1000);
//阻塞,只有当至少一个注册的事件发生的时候才会继续.
// selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
SelectionKey key = null;
while(it.hasNext()){
key = it.next();
it.remove();
try{
handleInput(key);
}catch(Exception e){
if(key != null){
key.cancel();
if(key.channel() != null){
key.channel().close();
}
}
}
}
}catch(Exception e){
e.printStackTrace();
System.exit(1);
}
}
//selector关闭后会自动释放里面管理的资源
if(selector != null)
try{
selector.close();
}catch (Exception e) {
e.printStackTrace();
}
}
private void handleInput(SelectionKey key) throws IOException{
if(key.isValid()){
SocketChannel sc = (SocketChannel) key.channel();
if(key.isConnectable()){
if(sc.finishConnect());
else System.exit(1);
}
//读消息
if(key.isReadable()){
//创建ByteBuffer,并开辟一个1M的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取请求码流,返回读取到的字节数
int readBytes = sc.read(buffer);
//读取到字节,对字节进行编解码
if(readBytes>0){
//将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
buffer.flip();
//根据缓冲区可读字节数创建字节数组
byte[] bytes = new byte[buffer.remaining()];
//将缓冲区可读字节数组复制到新建的数组中
buffer.get(bytes);
String result = new String(bytes,"UTF-8");
System.out.println("CLIENT GET MSG:" + result);
}
//没有读取到字节 忽略
// else if(readBytes==0);
//链路已经关闭,释放资源
else if(readBytes<0){
key.cancel();
sc.close();
}
}
}
}
//异步发送消息
private void doWrite(SocketChannel channel,String request) throws IOException{
//将消息编码为字节数组
byte[] bytes = request.getBytes();
//根据数组容量创建ByteBuffer
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
//将字节数组复制到缓冲区
writeBuffer.put(bytes);
//flip操作
writeBuffer.flip();
//发送缓冲区的字节数组
channel.write(writeBuffer);
//****此处不含处理“写半包”的代码
}
private void doConnect() throws IOException{
if(socketChannel.connect(new InetSocketAddress(host,port)));
else socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
public void sendMsg(String msg) throws Exception{
socketChannel.register(selector, SelectionKey.OP_READ);
doWrite(socketChannel, msg);
} }

Test:

package javanio;

import java.util.Scanner;

public class Test {
public static void main(String[] args) throws Exception{
//运行服务器
Server.start();
//避免客户端先于服务器启动前执行代码
Thread.sleep(100);
//运行客户端
Client.start();
while(Client.sendMsg(new Scanner(System.in).nextLine()));
} }

demo3:https://blog.csdn.net/Howinfun/article/details/81283721

【Netty整理03-NIO】Java 实现 NIO demo的更多相关文章

  1. Tinking in Java ---Java的NIO和对象序列化

    前面一篇博客的IO被称为经典IO,因为他们大多数都是从Java1.0开始就有了的:然后今天这篇博客是关于NIO的,所以的NIO其实就是JDK从1.4开始,Java提供的一系列改进的输入/输出处理的新功 ...

  2. Netty快速入门(03)Java NIO 介绍-Buffer

    NIO 介绍 NIO,可以说是New IO,也可以说是non-blocking IO,具体怎么解释都可以. NIO 1是在JSR51里面定义的,在JDK1.4中引入,因为BolckingIO不支持高并 ...

  3. 从I/O多路复用到Netty,还要跨过Java NIO包

    本文是Netty系列第4篇 上一篇文章我们深入了解了I/O多路复用的三种实现形式,select/poll/epoll. 那Netty是使用哪种实现的I/O多路复用呢?这个问题,得从Java NIO包说 ...

  4. 3. 彤哥说netty系列之Java BIO NIO AIO进化史

    你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...

  5. Netty | 第1章 Java NIO 网络编程《Netty In Action》

    目录 前言 1. Java 网络编程 1.1 Javs NIO 基本介绍 1.2 缓冲区 Buffer 1.2 通道 Channel 1.3 选择器 Selector 1.4 NIO 非阻塞网络编程原 ...

  6. 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

    1.引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本 ...

  7. 漫谈NIO(2)之Java的NIO

    1.前言 上章提到过Java的NIO采取的是多路IO复用模式,其衍生出来的模型就是Reactor模型.多路IO复用有两种方式,一种是select/poll,另一种是epoll.在windows系统上使 ...

  8. NIO 源码分析(03) 从 BIO 到 NIO

    目录 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel 和 Buffer 1.2 Selector 1.3 Linux IO 和 NIO 编程的区别 ...

  9. 一个小时就能理解Java的NIO必须掌握这三大要素!

    同步与阻塞 同步和异步是针对应用程序和内核的交互而言的. 同步:执行一个操作之后,进程触发IO操作并等待(阻塞)或者轮询的去查看IO的操作(非阻塞)是否完成,等待结果,然后才继续执行后续的操作. 异步 ...

随机推荐

  1. RTB业务知识2-Open-RTB全景

    一个.前言 openrtb这是一套开源的竞价广告系统,来自IAB贡献,井. 有许多值太借鉴,提供sdk api接口文档介绍,整理了相关的资料.主要包含其生态图体系.业务流程和基本的对象模型和数据模型. ...

  2. Java程序猿的书面采访String3

    public class SameString { //思想二:每个字符都相应着自己的ASC码,第一个思想的算法复杂度为O(nlogn).一般能够利用空间来减少时间复杂度 //能够开辟一个大小为256 ...

  3. C++异常机制的实现方式和开销分析 (大图,编译器会为每个函数增加EHDL结构,组成一个单向链表,非常著名的“内存访问违例”出错对话框就是该机制的一种体现)

    白杨 http://baiy.cn 在我几年前开始写<C++编码规范与指导>一文时,就已经规划着要加入这样一篇讨论 C++ 异常机制的文章了.没想到时隔几年以后才有机会把这个尾巴补完 :- ...

  4. WPF 左键单击弹出菜单 ContextMenu

    原文:WPF 左键单击弹出菜单 ContextMenu WPF中的ContextMenu在XAML中可直接做出来,但是仅限于右键弹出菜单,如果需要添加左键弹出功能,只需要在事件中添加Click事件 X ...

  5. ASP .NET My97DatePicker

    My97DatePicker http://jingyan.baidu.com/article/e6c8503c7244bae54f1a18c7.html <input type="t ...

  6. VS发布到IIS Express外网Debug(如微信开发)

    主要效果是本机调试网站,将网站发布到某域名(如m16758r728.iok.la),可以进入VS断点,不必再用远程调试!!! 环境 VS2015windows 10操作系统花生壳(可以用其他内网穿透的 ...

  7. 协程在Web服务器中的应用(配的图还不错)

    协程(纤程,微线程)这个概念早就有之,各家互联网公司也都有研究,但在国内各大论坛和大会热起来,还是今年的事. 最近参与讨论开放平台建设和架构设计过程中,有同事提到了使用协程代替线程,能够很大幅度的提高 ...

  8. WPF 绑定到集合

    <Window x:Class="CollectionBinding.MainWindow"        xmlns="http://schemas.micros ...

  9. 设置InputBox等提示框的字体以及样式

    InputBox等窗体的字体大小设置方法 Graphics.DefFontData.Height:=48;  Graphics.DefFontData.Style:=[fsBold,fsItalic, ...

  10. iOS UILabel显示html标签

    iOS7以后系统提供了显示html标签的方法 UIKIT_EXTERN NSString *const NSHTMLTextDocumentType NS_AVAILABLE_IOS(7_0); 直接 ...