这块还是挺复杂的,挺难理解,但是多练几遍,多看看研究研究其实也就那样,就是一个Selector轮询的过程,这里想要双向通信,客户端和服务端都需要一个Selector,并一直轮询,

直接贴代码:

Server:服务端:

package cn.hou.socket01._03nio01;

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; //nio 服务端
public class Server implements Runnable {
//1 多路复用器
private Selector selector;
//2 建立缓冲区
private ByteBuffer readBuf=ByteBuffer.allocate(1024);
private ByteBuffer writeBuf=ByteBuffer.allocate(1024);
//构造函数
public Server(int port){
try {
//1 打开多路复用器
this.selector=Selector.open();
//2 打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//3 设置服务器通道为非阻塞方式
ssc.configureBlocking(false);
//4 绑定ip
ssc.bind(new InetSocketAddress(port));
//5 把服务器通道注册到多路复用器上,只有非阻塞信道才可以注册选择器.并在注册过程中指出该信道可以进行Accept操作
ssc.register(this.selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器已经启动.....");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){//一直循环
try {
this.selector.select();//多路复用器开始监听
//获取已经注册在多了复用器上的key通道集
Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();
//遍历
while (keys.hasNext()) {
SelectionKey key = keys.next();//获取key
//如果是有效的
if(key.isValid()){
// 如果为阻塞状态,一般是服务端通道
if(key.isAcceptable()){
this.accept(key);
}
// 如果为可读状态,一般是客户端通道
if(key.isReadable()){
this.read(key);
}
}
//从容器中移除处理过的key
keys.remove();
}
} catch (IOException e) {
e.printStackTrace();
}
} }
//从客户端通道获取数据并进行处理
private void read(SelectionKey key) {
try {
//1 清空缓冲区旧的数据
this.readBuf.clear();
//2 获取之前注册的socket通道对象
SocketChannel sc = (SocketChannel) key.channel();
//3 读取数据
int count = sc.read(this.readBuf);
//4 如果没有数据
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
this.readBuf.flip();
//6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[this.readBuf.remaining()];
//7 接收缓冲区数据
this.readBuf.get(bytes);
//8 打印结果
String body = new String(bytes).trim();
System.out.println("服务端接受到客户端请求的数据: " + body);
//9 告诉客户端已收到数据
writeBuf.put("你好,客户端,我已收到数据".getBytes());
//对缓冲区进行复位
writeBuf.flip();
//写出数据到服务端
sc.write(writeBuf);
//清空缓冲区数据
writeBuf.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
//接受一个客户端socket进行处理
private void accept(SelectionKey key) {
try {
//1 获取服务通道
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//2 执行阻塞方法,当有客户端请求时,返回客户端通信通道
SocketChannel sc = ssc.accept();
//3 设置阻塞模式
sc.configureBlocking(false);
//4 注册到多路复用器上,并设置可读标识
sc.register(this.selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
} } public static void main(String[] args) {
//启动服务器
new Thread(new Server(9527)).start();
} }

  Client客户端:

package cn.hou.socket01._03nio01;

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; //nio 客户端
public class Client{
//客户端信道选择器,轮询读取服务端返回数据
private Selector selector;
//连接信道
private SocketChannel sc;
public Client(){
try {
this.sc=SocketChannel.open();//打开信道
sc.connect(new InetSocketAddress("127.0.0.1",9527));////连接服务端
sc.configureBlocking(false);//设置非阻塞
selector = Selector.open();//必须打开
//将当前客户端注册到多路复用器上,并设置为可读状态
sc.register(this.selector, SelectionKey.OP_READ);
//开启线程,一直轮询
new Thread(()->{
while(true){//一直循环
try {
this.selector.select();//多路复用器开始监听
//获取已经注册在多了复用器上的key通道集
Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();
//遍历
while (keys.hasNext()) {
SelectionKey key = keys.next();//获取key
//如果是有效的
if(key.isValid()){
// 如果为可读状态,读取服务端返回的数据
if(key.isReadable()){
this.read(key);
}
}
//从容器中移除处理过的key
keys.remove();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
} //客户端获取服务端返回的数据
private void read(SelectionKey key) {
try {
//建立写缓冲区
ByteBuffer readBuf = ByteBuffer.allocate(1024);
//2 获取之前注册的socket通道对象
SocketChannel sc = (SocketChannel) key.channel();
//3 读取数据
int count = sc.read(readBuf);
//4 如果没有数据
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
readBuf.flip();
//6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[readBuf.remaining()];
//7 接收缓冲区数据
readBuf.get(bytes);
//8 打印结果
String body = new String(bytes).trim();
System.out.println("客户端已接受到服务端返回的数据: " + body);
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
//建立写缓冲区
ByteBuffer writebuf = ByteBuffer.allocate(1024);
Client client = new Client();
try {
while(true){
//定义一个字节数组,然后使用系统录入功能:
byte[] bytes = new byte[1024];
System.in.read(bytes);
//把数据放到缓冲区中
writebuf.put(bytes);
//对缓冲区进行复位
writebuf.flip();
//写出数据到服务端
client.sc.write(writebuf);
//清空缓冲区数据
writebuf.clear();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(client.sc != null){
try {
client.sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

  ,先启动服务端,然后再启动客户端:

效果如下:

Server:

Client:

基于NIO的同步非阻塞编程完整案例,客户端发送请求,服务端获取数据并返回给客户端数据,客户端获取返回数据的更多相关文章

  1. Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程

    Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程 缓冲区(Buffer) 用于存储数据 通道(Channel) 用于传输数据 多路复用器(Selector) 用于轮询 Channel 状 ...

  2. NIO【同步非阻塞io模型】关于 NIO socket 的详细总结【Java客户端+Java服务端 + 业务层】【可以客户端间发消息】

    1.前言 以前使用 websocket来实现双向通信,如今深入了解了 NIO 同步非阻塞io模型 , 优势是 处理效率很高,吞吐量巨大,能很快处理大文件,不仅可以 做 文件io操作, 还可以做sock ...

  3. NIO【同步非阻塞io模型】关于 文件io 的总结

    1.前言 这一篇随笔是写 NIO 关于文件输入输出的总结 /* 总结: 1.io操作包括 socket io ,file io ; 2.在nio模型,file io使用fileChannel 管道 , ...

  4. java的高并发IO原理,阻塞BIO同步非阻塞NIO,异步非阻塞AIO

    原文地址: IO读写的基础原理 大家知道,用户程序进行IO的读写,依赖于底层的IO读写,基本上会用到底层的read&write两大系统调用.在不同的操作系统中,IO读写的系统调用的名称可能不完 ...

  5. 如何解读 Java IO、NIO 中的同步阻塞与同步非阻塞?

    原文链接:如何解读 Java IO.NIO 中的同步阻塞与同步非阻塞? 一.前言 最近刚读完一本书:<Netty.Zookeeper.Redis 并发实战>,个人觉得 Netty 部分是写 ...

  6. IO同步阻塞与同步非阻塞

    BIO.NIO.AIO IO(BIO)和NIO区别:其本质就是阻塞和非阻塞的区别 阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,就会一直等待,直到传输完毕为止. 非阻塞概念:应用程序直 ...

  7. IO通信模型(二)同步非阻塞模式NIO(NonBlocking IO)

    同步非阻塞模式(NonBlocking IO) 在非阻塞模式中,发出Socket的accept()和read()操作时,如果内核中的数据还没有准备好,那么它并不会阻塞用户进程,而是立刻返回一个信息.也 ...

  8. 同步异步阻塞非阻塞Reactor模式和Proactor模式 (目前JAVA的NIO就属于同步非阻塞IO)

    在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...

  9. NIO:异步非阻塞I/O,AIO,BIO

    Neety的基础使用及说明 https://www.cnblogs.com/rrong/p/9712847.html BIO(缺乏弹性伸缩能力,并发量小,容易出现内存溢出,出现宕机 每一个客户端对应一 ...

随机推荐

  1. java中new两个对象,在堆中开辟几个对象空间

    内存堆中有两个对象,两个对象里都有独立的变量.p1 p2指向的不是同一个内存空间. 也可以这样描述引用p1,p2指向两个不同的对象.

  2. 【比赛】NOIP2017 宝藏

    这道题考试的时候就骗了部分分.其实一眼看过去,n范围12,就知道是状压,但是不知道怎么状压,想了5分钟想不出来就枪毙了状压,与AC再见了. 现在写的是状压搜索,其实算是哈希搜索,感觉状压DP理解不了啊 ...

  3. BZOJ 3110 K大数查询 | 整体二分

    BZOJ 3110 K大数查询 题面 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个 ...

  4. 第三周 构造一个简单的Linux系统

    20135331文艺 首先 在上周内容中我们学习了 计算机三个法宝: 1.存储程序计算机 2.函数调用堆栈 3.中断 本周中得知 操作系统两把宝剑: 1.中断上下文的切换:保存现场和恢复现场 2.进程 ...

  5. activity 与 service 之间的通信

    activity和service通信:通过binder 举个我实际项目中的例子:在service中下载更新应用 首先是下载更新apk的service: public class UpdateVersi ...

  6. Codeforces 914F. Substrings in a String(bitset)

    比赛的时候怎么没看这题啊...血亏T T 对每种字符建一个bitset,修改直接改就好了,查询一个区间的时候对查询字符串的每种字符错位and一下,然后用biset的count就可以得到答案了... # ...

  7. mac下Android Studio使用及快捷键

    1.Android Studio使用及快捷键 (1)我们新建一个项目后进入界面,左侧可以选择Project或Android,一般选Project会比较习惯以前eclipse的显示 (2)点击左上角An ...

  8. 【线段树】【P4198】 楼房重建

    Description 小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度.如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没 ...

  9. navicat执行大容量的.sql文件时的设置

    如果有主外键关联等,执行报错,则去掉中间的对勾保留第三个对勾试试.第三个对勾 是 手动提交(不自动提交,估计是全部导入到数据库中之后再一起提交,而不是导入一条sql语句就提交一次) 如果同时不勾选第2 ...

  10. Python Opencv 色彩平衡

    在平衡良好的照片中,最亮的颜色应该是白色和最暗的黑色. 因此,我们可以通过缩放每个R,G和B通道的直方图来移除图像中的色偏,使得它们跨越完整的0-255比例 网上只有C的代码 修改为Python代码 ...