java nio demo_【Netty整理03-NIO】Java 实现 NIO demo

jdk提供的NIO使用:

博主抄写了网上的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 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 keys = selector.selectedKeys();

Iterator 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 keys = selector.selectedKeys();

Iterator 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()));

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值