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. IOC学习1

    学习蒋金楠的 ASP.NET Core中的依赖注入(1):控制反转(IoC) 而来,这篇文章经典异常.一定要多读.反复读. 这篇文章举了一个例子,就是所谓的mvc框架,一开始介绍mvc的思想,由一个d ...

  2. 简明Python3教程 首页

    A Byte of Python 'A Byte of Python' is a free book on programming using the Python language. It serv ...

  3. 微软输入法TSF SampleIME 代码浅说

    原文:微软输入法TSF SampleIME 代码浅说 微软提供了一个简单的TSF拼音输入法的示例SampleIME,主要的问题是:不支持数字键选候选字,不能自动上屏,没有联想功能.在笔者开发动态输入法 ...

  4. debian9 安装 odoo11 笔记用 部分内容转载前辈的,在此感谢

    1先创建个odoo用户 sudo adduser odoo 2:给root 权限: sudo vi /etc/sudoers 修改文件参考如下: # User privilege specificat ...

  5. window下nodejs爬取gb2312网页出现乱码的解决方案

     发布于 2012-8-22 18:15  5230 次浏览  最后一次编辑是 2013-2-18 22:31 linux环境下,我们可以通过 iconv 这个C++模块来处理Node.JS不支持的字 ...

  6. WPF4.0用tablet实现手写输入(更新XP SP3下也能手写输入方法)

    原文:WPF4.0用tablet实现手写输入(更新XP SP3下也能手写输入方法) 由于项目需求一个手写输入的控件,纠结了2天,终于搞定了. 主要是由于本人的英语不过关,一直和ocr混淆在一起,研究了 ...

  7. python 安装 win 下的exe结尾的文件操作

    1.首先下载相关的模块 2.把下载的相关模块放到python 安装目录下 3.cmd 切换到python的安装目录下 例如:{PIL-1.1.7.win32-py2.7.exe} 4.执行 pip i ...

  8. LIBCMTD.lib(exe_winmain.obj) : error LNK2019: 无法解析的外部符号 _WinMain@16,该符号在函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中被引用

    这个问题是没找到程序入口 在网上查这个问题,一般都是说两条: (若是win32程序) 一是在项目属性\CC++\预处理器\预处理器定义\里添加 _WINDOWS 一是在项目属性\链接\系统 里选择 窗 ...

  9. 错误代码: 0x800700b7 配置错误:定义了重复的“system.web.extensions/scripting/scriptResourceHandler”节

    操作环境:Windows 8.1  IIS8.5 VS2008 解决方法: 把应用程序池的.Net CLR 版本从4.0修改为2.0即可.如下图所示:

  10. 将多个文本文件内的数据导入到Datagridview

    private BindingList listXSxxInfoList = new BindingList(); openFileDialog1.Multiselect = true;//允许选择多 ...