NIO学习
1. NIO客户端与服务端网络编程关键:
理解各个监听事件的驱动事件,总结以下几点:
(1)ServerSocketChannel注册了OP_ACCEPT事件,需要客户端发起连接请求,服务端selector才能监听到(阻塞在selector.select()才能继续执行)
(2)服务端获取客户端SocketChannel,注册读事件后,只有客户端写事件,服务端selector才能监听到(阻塞在selector.select()才能继续执行,读事件是被动的,需要写事件驱动)
(3)不管服务端还是客户端,本端注册的管道写事件,selector马上能监听到(总结:写事件是主动的,只要注册,就能监听到)
(4)SelectionKey.OP_CONNECT(主动事件,客户端注册完之后,本端selector马上能监听到)-->isConnectable()为true
SelectionKey.OP_ACCEPT(被动事件,需服务端注册accepte事件,还需要客户端发连接请求触发)-->isAcceptable()为true
SelectionKey.OP_WRITE(主动事件,客户端注册完之后,本端selector马上能监听到)-->isWritable()为true
SelectionKey.OP_READ(被动事件,需本端注册读事件,并且对端有写事件才触发)-->isReadable()为true
2. 服务端代码:
package com.hw.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.Set;
/**
* NIO客户端与服务端网络编程关键:
* 理解各个监听事件的驱动事件,例如:
* (1)ServerSocketChannel注册了OP_ACCEPT事件,需要客户端发起连接请求,服务端selector才能监听到(阻塞在selector.select()才能继续执行)
* (2)服务端获取客户端SocketChannel,注册读事件后,只有客户端写事件,服务端selector才能监听到(阻塞在selector.select()才能继续执行)(总结:读事件是被动的,需要写事件驱动)
* (3)不管服务端还是客户端,本端注册的管道写事件,selector马上能监听到(总结:写事件是主动的,只要注册,就能监听到)
* (4)SelectionKey.OP_CONNECT(主动事件,客户端注册完之后,本端selector马上能监听到)-->isConnectable()为true
* SelectionKey.OP_ACCEPT(被动事件,需服务端注册accepte事件,还需要客户端发连接请求触发)-->isAcceptable()为true
* SelectionKey.OP_WRITE(主动事件,客户端注册完之后,本端selector马上能监听到)-->isWritable()为true
* SelectionKey.OP_READ(被动事件,需本端注册读事件,并且对端有写事件才触发)-->isReadable()为true
* @author thinkpad
*
*/
public class NIOServer { /*标识数字*/
private int flag = 0;
/*缓冲区大小*/
private int BLOCK = 4096;
/*接受数据缓冲区*/
private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
/*发送数据缓冲区*/
private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
private Selector selector; public NIOServer(int port) throws IOException {
// 打开服务端套接字通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 服务器配置为非阻塞
serverSocketChannel.configureBlocking(false);
// 检索与此通道关联的服务器套接字
ServerSocket serverSocket = serverSocketChannel.socket();
// 进行服务的绑定
serverSocket.bind(new InetSocketAddress(port));
// 通过open()方法找到Selector
selector = Selector.open();
// 注册通道到selector,等待连接
// self:1.如果不注册OP_ACCEPT,即使客户端发连接请求,selector也无法监听到连接事件,导致selector.select()一直阻塞等待
// 2.即使注册了OP_ACCEPTE,客户端如果不发送连接请求,服务端也会一直阻塞在selector.select();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server Start----8888:");
} // 监听
private void listen() throws IOException {
while (true) {
// 选择一组键,并且相应的通道已经打开(服务端如果注册了OP_ACCEPTE,第一次等待客户端连接,若客户端无连接请求,阻塞在此处)
selector.select();
// 返回此选择器的已选择键集。
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
handleKey(selectionKey);
}
}
} // 处理请求
private void handleKey(SelectionKey selectionKey) throws IOException {
// 接受请求
ServerSocketChannel server = null;
SocketChannel client = null;
String receiveText;
String sendText;
int count=0;
// 测试此键的通道是否已准备好接受新的套接字连接。
if (selectionKey.isAcceptable()) {
// 返回为之创建此键的通道。
server = (ServerSocketChannel) selectionKey.channel();
// 接受到此通道套接字的连接。
// 此方法返回的套接字通道(如果有)将处于阻塞模式。
client = server.accept();
// 配置为非阻塞
client.configureBlocking(false);
// 注册到selector,等待连接
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) { // 仅仅注册了读事件还不行,还需要客户端写才能触发
// 返回为之创建此键的通道。
client = (SocketChannel) selectionKey.channel();
//将缓冲区清空以备下次读取
receivebuffer.clear();
//读取服务器发送来的数据到缓冲区中
count = client.read(receivebuffer);
if (count > 0) {
receiveText = new String( receivebuffer.array(),0,count);
System.out.println("服务器端接受客户端数据--:"+receiveText);
client.register(selector, SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()) {
//将缓冲区清空以备下次写入
sendbuffer.clear();
// 返回为之创建此键的通道。
client = (SocketChannel) selectionKey.channel();
sendText="message from server--" + flag++;
//向缓冲区中输入数据
sendbuffer.put(sendText.getBytes());
//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
sendbuffer.flip();
//输出到通道
client.write(sendbuffer);
System.out.println("服务器端向客户端发送数据--:"+sendText);
client.register(selector, SelectionKey.OP_READ);
}
} /**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
int port = 8888;
NIOServer server = new NIOServer(port);
server.listen();
}
}
3. 客户端代码:
package com.hw.nio; 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 NIOClient { /*标识数字*/
private static int flag = 0;
/*缓冲区大小*/
private static int BLOCK = 4096;
/*接受数据缓冲区*/
private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
/*发送数据缓冲区*/
private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
/*服务器端地址*/
private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
"localhost", 8888); public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
// 打开socket通道
SocketChannel socketChannel = SocketChannel.open();
// 设置为非阻塞方式
socketChannel.configureBlocking(false);
// 打开选择器
Selector selector = Selector.open();
// 注册连接服务端socket动作(self:只有将管道注册到selector上,selector.select()才会执行,否则阻塞,selector只会监听本端的
// channel)
socketChannel.register(selector, SelectionKey.OP_CONNECT);
// 连接
socketChannel.connect(SERVER_ADDRESS);
// 分配缓冲区大小内存 Set<SelectionKey> selectionKeys;
Iterator<SelectionKey> iterator;
SelectionKey selectionKey;
SocketChannel client;
String receiveText;
String sendText;
int count=0; while (true) {
//选择一组键,其相应的通道已为 I/O 操作准备就绪。
//此方法执行处于阻塞模式的选择操作。
selector.select();
//返回此选择器的已选择键集。
selectionKeys = selector.selectedKeys();
//System.out.println(selectionKeys.size());
iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
System.out.println("client connect");
client = (SocketChannel) selectionKey.channel();
// 判断此通道上是否正在进行连接操作。
// 完成套接字通道的连接过程。
if (client.isConnectionPending()) {
client.finishConnect();
System.out.println("完成连接!");
sendbuffer.clear();
sendbuffer.put("Hello,Server".getBytes());
sendbuffer.flip();
// sendbuffer = null;如果写个空,服务端也不理
client.write(sendbuffer);
}
// self:向服务器端写完数据,用于客户端Socket管道注册读事件,如果本端(客户端)selector监听到服务端向客户端写数据,阻塞在selector.select()处的代码继续执行,则可以读取数据
// 本端注册读事件,是为了响应对端的写事件
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
client = (SocketChannel) selectionKey.channel();
//将缓冲区清空以备下次读取
receivebuffer.clear();
//读取服务器发送来的数据到缓冲区中
count=client.read(receivebuffer);
if(count>0){
receiveText = new String( receivebuffer.array(),0,count);
System.out.println("客户端接受服务器端数据--:"+receiveText);
// self:写事件注册完之后,selector马上能监听到,本端的写事件是主动的发起,读事件需要对端的写事件驱动
client.register(selector, SelectionKey.OP_WRITE);
} } else if (selectionKey.isWritable()) {
sendbuffer.clear();
client = (SocketChannel) selectionKey.channel();
sendText = "message from client--" + (flag++);
sendbuffer.put(sendText.getBytes());
//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
sendbuffer.flip();
client.write(sendbuffer);
System.out.println("客户端向服务器端发送数据--:"+sendText);
client.register(selector, SelectionKey.OP_READ);
}
}
selectionKeys.clear();
}
}
}
4. 测试代码:
package com.hw.nio; import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.TimeUnit; public class NIOTest { public static void client() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost", 8080)); if (socketChannel.finishConnect()) {
int i = 0;
while (true) {
TimeUnit.SECONDS.sleep(1);
String info = "I'm " + i++ + "-th information from client";
buffer.clear();
buffer.put(info.getBytes());
buffer.flip();
while (buffer.hasRemaining()) {
System.out.println(buffer);
socketChannel.write(buffer);
}
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} finally {
try {
if (socketChannel != null) {
socketChannel.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} public static void server() {
ServerSocket serverSocket = null;
InputStream in = null;
try {
serverSocket = new ServerSocket(8080);
int recvMsgSize = 0;
byte[] recvBuf = new byte[1024];
while (true) {
Socket clntSocket = serverSocket.accept();
SocketAddress clientAddress = clntSocket.getRemoteSocketAddress();
System.out.println("Handling client at " + clientAddress);
in = clntSocket.getInputStream();
while ((recvMsgSize = in.read(recvBuf)) != -1) {
byte[] temp = new byte[recvMsgSize];
System.arraycopy(recvBuf, 0, temp, 0, recvMsgSize);
System.out.println(new String(temp));
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
if("client".equals(args[0])) {
client();
}
server();
if("server".equals(args[0])) {
server();
}
}
}
NIO学习的更多相关文章
- JAVA NIO学习一:NIO简介、NIO&IO的主要区别
在前面学习了IO之后,今天我们开始进入NIO学习环节,首先我们会NIO做一个简单的介绍,让大家认识NIO,然后会和IO进行一个对比认识进行区分.好了,下面我们就开始学习: 一.NIO简介 1.概述 从 ...
- Java NIO学习与记录(八): Reactor两种多线程模型的实现
Reactor两种多线程模型的实现 注:本篇文章例子基于上一篇进行:Java NIO学习与记录(七): Reactor单线程模型的实现 紧接着上篇Reactor单线程模型的例子来,假设Handler的 ...
- Java NIO学习笔记
Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
- Java NIO 学习笔记(七)----NIO/IO 的对比和总结
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(四)----文件通道和网络通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(三)----Selector
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(二)----聚集和分散,通道到通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
随机推荐
- Loj #3089. 「BJOI2019」奥术神杖
Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...
- win7系统IE浏览器主页被搜狗篡改问题的解决方法
IE浏览器使用一段时间后可能大家就会遇到主页被篡改的问题,篡改之后主页就变成了搜狗页面,我们常用的百度搜索也变成了搜狗搜索,这不仅使得我们操作起 来不习惯,使用起来也会感觉非常别扭.那如果在使用IE浏 ...
- centos7下给bond网卡配置bridge桥接
这篇的主题可以用几个关键字组合:centos7+kvm + bond + bridge .brige主要用在KVM虚拟化环境下,而bond是进行物理层面的冗余.具体配置信息如下 物理网卡名称:enp0 ...
- Windows rundll32的用法-批处理管理打印机
用法: rundll32 printui.dll,PrintUIEntry [options] [@commandfile] /a[file] 二进制文件名 /b[name] 基本打印机名 /c[na ...
- Tutorial 02_熟悉常用的HDFS操作
Shell命令实现: (1)向HDFS 中上传任意文本文件,如果指定的文件在HDFS 中已经存在,则由用户来指定是追加到原有文件末尾还是覆盖原有的文件: (2) 从HDFS 中下载指定文件,如果本地文 ...
- 深度学习识别CIFAR10:pytorch训练LeNet、AlexNet、VGG19实现及比较(三)
版权声明:本文为博主原创文章,欢迎转载,并请注明出处.联系方式:460356155@qq.com VGGNet在2014年ImageNet图像分类任务竞赛中有出色的表现.网络结构如下图所示: 同样的, ...
- vue.js实战——props数据验证(自定义构造器检测)
Vue.component('my-component',{ props:{ //必须是数字类型 propA:Number, //必须是字符串或数字类型 propB:[String,Number], ...
- String.Join Method
Overloads Join(String, String[], Int32, Int32) Concatenates the specified elements of a string array ...
- xgboost 非官方每天编译
xgboost http://ssl.picnet.com.au/xgboost/ 非官方每天编译
- OpenStack 命令行速查表
OpenStack 命令行速查表 updated: 2017-07-18 08:53 Contents 认证 (keystone) 镜像(glance) 计算 (nova) 实例的暂停.挂起.停止 ...