Channel(通道)
ServerSocketChannel和SocketChannel创建连接
同步阻塞实现
/*
实现同步非阻塞的服务器
相关的类:
java.nio.channels.ServerSocketChannel:针对面向流的侦听套接字的可选择通道。
获取对象的方式:
static ServerSocketChannel open() 打开服务器套接字通道。
成员方法:
ServerSocketChannel bind(SocketAddress local) 给服务器绑定端口号
SocketChannel accept() 监听客户端的请求。
SelectableChannel configureBlocking(boolean block) 设置阻塞模式,true:阻塞; false:非阻塞
实现步骤:
1.获取ServerSocketChannel对象
2.使用ServerSocketChannel对象中的bind绑定指定的端口号
3.使用ServerSocketChannel对象中的accept方法监听客户端的请求
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.获取ServerSocketChannel对象
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.使用ServerSocketChannel对象中的bind绑定指定的端口号
serverSocketChannel.bind(new InetSocketAddress(8888));
//3.使用ServerSocketChannel对象中的accept方法监听客户端的请求
System.out.println("服务器等待客户端连接...");
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("有客户端连接服务器...");
}
}
/*
实现同步非阻塞的客户端
相关的类:
java.nio.channels.SocketChannel
获取对象的方法:
static SocketChannel open() 打开套接字通道。
成员方法:
boolean connect(SocketAddress remote) 根据服务器的ip地址和端口号连接服务器
configureBlocking(boolean block)设置阻塞模式,true:阻塞; false:非阻塞
实现步骤:
1.获取SocketChannel对象
2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.获取SocketChannel对象
SocketChannel socketChannel = SocketChannel.open();
//2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
}
}
服务器轮询监听客户端
public class TCPServer {
public static void main(String[] args) throws IOException, InterruptedException {
//1.获取ServerSocketChannel对象
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.使用ServerSocketChannel对象中的bind绑定指定的端口号
serverSocketChannel.bind(new InetSocketAddress(8888));
//3.使用ServerSocketChannel对象中的accept方法监听客户端的请求
//设置服务器非阻塞模式
serverSocketChannel.configureBlocking(false);
//轮询监听客户端
while (true){
System.out.println("服务器等待客户端连接...");
SocketChannel socketChannel = serverSocketChannel.accept();//默认阻塞
//对象SocketChannel进行判断,没有客户端连接返回null
if(socketChannel!=null){
System.out.println("有客户端连接服务器...");
break;//结束轮询
}else{
System.out.println("没有客户端连接服务器,休息2秒,干点其他事,继续轮询...");
Thread.sleep(2000);
}
}
}
}
执行结果
服务器等待客户端连接...
没有客户端连接服务器,休息2秒,继续轮询...
服务器等待客户端连接...
没有客户端连接服务器,休息2秒,继续轮询...
服务器等待客户端连接...
有客户端连接服务器...
客户端实现同步非阻塞
/*
实现同步非阻塞的客户端
相关的类:
java.nio.channels.SocketChannel
获取对象的方法:
static SocketChannel open() 打开套接字通道。
成员方法:
boolean connect(SocketAddress remote) 根据服务器的ip地址和端口号连接服务器
configureBlocking(boolean block)设置阻塞模式,true:阻塞; false:非阻塞
实现步骤:
1.获取SocketChannel对象
2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
注意:
客户端使用connect连接服务器
客户端设置为阻塞模式,会多次尝试连接,连接成功connect方法返回true连接不成功抛出连接异常
客户端设置为非阻塞模式,只会连接一次服务器,connect方法返回false
*/
public class TCPClient {
public static void main(String[] args) throws InterruptedException {
//阻塞轮询连接服务器
while (true){
try{
//1.获取SocketChannel对象
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);//默认true:阻塞
//socketChannel.configureBlocking(false);//false:非阻塞
//2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
boolean b = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
System.out.println(b);
System.out.println("连接服务器成功,结束轮询...");
break;//结束轮询
}catch (Exception e){
System.out.println("连接服务器失败,休息2秒,干点别的...");
Thread.sleep(2000);
}
}
}
}
ServerSocketChannel和SocketChannel收发信息
/*
客户端给服务器发送数据,读取服务器回写的数据
int write(ByteBuffer src) 给服务器发送数据
int read(ByteBuffer dst) 读取服务器发送的数据
*/
public class TCPClient {
public static void main(String[] args) throws IOException, InterruptedException {
//轮询连接服务器
while (true){
try {
//1.使用SocketChannel中的方法open获取SocketChannel的子类对象
SocketChannel socketChannel = SocketChannel.open();
//设置客户端的阻塞模式
socketChannel.configureBlocking(true);
//socketChannel.configureBlocking(false);
//2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
boolean b = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
//System.out.println(b);
//int write(ByteBuffer src) 给服务器发送数据
ByteBuffer buffer = ByteBuffer.wrap("你好服务器".getBytes());
System.out.println("容量:"+buffer.capacity());
System.out.println("索引:"+buffer.position());
System.out.println("限定:"+buffer.limit());
socketChannel.write(buffer);
//int read(ByteBuffer dst) 读取服务器发送的数据
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
int len = socketChannel.read(buffer2);
buffer2.flip();//缩小limit的范围
String msg = new String(buffer2.array(), 0, buffer2.limit());
System.out.println("客户端收到服务器发送的数据:"+msg);
System.out.println("连接服务器成功,结束轮询...");
socketChannel.close();
break;
} catch (IOException e) {
System.out.println("连接服务器失败休息2秒,干点别的,继续轮询...");
Thread.sleep(2000);
}
}
}
}
/*
服务器接收客户端发送的数据,给客户端回写数据
int read(ByteBuffer dst) 读取客户端发送的数据
int write(ByteBuffer src) 给客户端发送(回写)数据
*/
public class TCPServer {
public static void main(String[] args) throws IOException, InterruptedException {
//1.使用ServerSocketChannel中的方法open获取一个ServerSocketChannel的子类对象
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.使用ServerSocketChannel对象中的bind方法绑定指定的端口号
serverSocketChannel.bind(new InetSocketAddress(8888));
//3.使用ServerSocketChannel对象中的accept方法监听客户端的请求
//设置服务器的非阻塞模式
serverSocketChannel.configureBlocking(false);
//轮询监听客户端
while (true){
System.out.println("服务器等待客户端连接....");
SocketChannel socketChannel = serverSocketChannel.accept();
//对SocketChannel进行判断:accept没有获取到客户端对象默认值null
if(socketChannel!=null){
System.out.println("有客户端连接服务器....");
//int read(ByteBuffer dst) 读取客户端发送的数据
ByteBuffer buffer1 = ByteBuffer.allocate(1024);
int len = socketChannel.read(buffer1);
buffer1.flip();//缩小limit的范围
String msg = new String(buffer1.array(), 0, buffer1.limit());
System.out.println("服务器读取客户端发送的数据是:"+msg);
//int write(ByteBuffer src) 给客户端发送(回写)数据
socketChannel.write(ByteBuffer.wrap("收到谢谢".getBytes()));
break;//跳出循环,结束轮询
}else{
System.out.println("没有客户端连接服务器,休息2秒,干点其他事情,继续轮询...");
Thread.sleep(2000);
}
}
//释放资源
serverSocketChannel.close();
}
}
Selector(选择器)
多路复用概念
选择器Selector是NIO中的重要技术之一。它与SelectableChannel联合使用实现了非阻塞的多路复用。使用它可以节省CPU资源,提高程序的运行效率。
"多路"是指:服务器端同时监听多个“端口”的情况。每个端口都要监听多个客户端的连接。
- 服务器端的非多路复用效果
如果不使用“多路复用”,服务器端需要开很多线程处理每个端口的请求。如果在高并发环境下,造成系统性能下降。 - 服务器端的多路复用效果
使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段业务环境下有非常重要的优势
选择器Selector——服务器端实现多路注册
/*
选择器Selector_服务器端实现多路注册
java.nio.channels.Selector:SelectableChannel对象的多路复用器。
获取Selector对象的方式:
static Selector open() 打开选择器。
注册Channel到Selector:ServerSocketChannel中的方法
SelectionKey register(Selector,SelectionKey.OP_READ);
参数:
SelectionKey:意思是在通过Selector监听Channel时对什么事件感兴趣
接收就绪--常量:SelectionKey.OP_ACCEPT (ServerSocketChannel在注册时只能使用此项)
实现步骤:
1.创建3个ServerSocketChannel对象
2.分别给3个ServerSocketChannel对象绑定不同的端口号
3.设置3个ServerSocketChannel对象为非阻塞
4.获取Selector对象
5.把3个ServerSocketChannel对象注册到Selector对象
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建3个ServerSocketChannel对象
ServerSocketChannel channel = ServerSocketChannel.open();
ServerSocketChannel channe2 = ServerSocketChannel.open();
ServerSocketChannel channe3 = ServerSocketChannel.open();
//2.分别给3个ServerSocketChannel对象绑定不同的端口号
channel.bind(new InetSocketAddress(7777));
channe2.bind(new InetSocketAddress(8889));
channe3.bind(new InetSocketAddress(9999));
//3.设置3个ServerSocketChannel对象为非阻塞
channel.configureBlocking(false);
channe2.configureBlocking(false);
channe3.configureBlocking(false);
//4.获取Selector对象
Selector selector = Selector.open();
//5.把3个ServerSocketChannel对象注册到Selector对象
channel.register(selector, SelectionKey.OP_ACCEPT);
channe2.register(selector, SelectionKey.OP_ACCEPT);
channe3.register(selector, SelectionKey.OP_ACCEPT);
}
}
选择器Selector——常用方法
- Selector的keys()方法**
- 此方法返回一个Set<SelectionKey>集合,表示:已注册通道的集合。每个已注册通道封装为一个SelectionKey对象。
- **Selector的selectedKeys()方法**
- 此方法返回一个Set<SelectionKey>集合,表示:当前已连接的通道的集合。每个已连接通道同一封装为一个SelectionKey对象。
- **Selector的select()方法**
- 此方法会阻塞,直到至少有1个客户端连接。
- 此方法会返回一个int值,表示有几个客户端连接了服务器。
public class TCPServer {
public static void main(String[] args) throws IOException, InterruptedException {
//1.创建3个ServerSocketChannel对象
ServerSocketChannel channel = ServerSocketChannel.open();
ServerSocketChannel channe2 = ServerSocketChannel.open();
ServerSocketChannel channe3 = ServerSocketChannel.open();
//2.分别给3个ServerSocketChannel对象绑定不同的端口号
channel.bind(new InetSocketAddress(7777));
channe2.bind(new InetSocketAddress(8888));
channe3.bind(new InetSocketAddress(9999));
//3.设置3个ServerSocketChannel对象为非阻塞
channel.configureBlocking(false);
channe2.configureBlocking(false);
channe3.configureBlocking(false);
//4.获取Selector对象
Selector selector = Selector.open();
//5.把3个ServerSocketChannel对象注册到Selector对象
channel.register(selector, SelectionKey.OP_ACCEPT);
channe2.register(selector, SelectionKey.OP_ACCEPT);
channe3.register(selector, SelectionKey.OP_ACCEPT);
//循环接收客户端的连接
while (true){
//Selector的select()方法:获取连接客户端的数量,没有获取到客户端的连接对象,此方法将阻塞
int count = selector.select();
System.out.println("连接客户端的数量:"+count);
//Selector的keys()方法:获取已经注册的通道集合
Set<SelectionKey> keys = selector.keys();
System.out.println("已经注册的通道的数量:"+keys.size());
//Selector的selectedKeys()方法:获取已经连接的通道集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
System.out.println("已经连接的通道的数量:"+selectionKeys.size());
//获取完一个客户端的连接,睡眠2秒
Thread.sleep(2000);
}
}
}
/*
创建两个线程,连接服务器
*/
public class TCPClient {
public static void main(String[] args) {
//启动两个线程,分别连接7777和8888端口
new Thread(()->{
while (true) {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("7777客户端连接服务器......");
socket.connect(new InetSocketAddress("localhost", 7777));
System.out.println("7777客户端连接成功....");
break;
} catch (IOException e) {
System.out.println("7777异常重连");
}
}
}).start();
new Thread(()->{
while (true) {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("8888客户端连接服务器......");
socket.connect(new InetSocketAddress("localhost", 8888));
System.out.println("8888客户端连接成功....");
break;
} catch (IOException e) {
System.out.println("8888异常重连");
}
}
}).start();
}
}
选择器Selector——多路信息接收
/*
使用选择器Selector:服务器端实现多路复用
java.nio.channels.Selector:SelectableChannel 对象的多路复用器。
获取Selector对象的方式:
static Selector open() 打开一个选择器。
注册Channel到Selector中:ServerSocketChannel中的方法:
SelectionKey register(Selector sel, SelectionKey.OP_ACCEPT)
参数:
SelectionKey:在通过Selector监听Channel时对什么事件感兴趣
SelectionKey.OP_ACCEPT:常量-->ServerSocketChanne在注册时只能使用此项
实现步骤:
1.创建3个ServerSocketChannel对象
2.分别给3个ServerSocketChannel对象绑定不同的端口号
3.设置3个ServerSocketChannel对象为非阻塞
4.获取Selector对象
5.使用3个ServerSocketChannel对象中的方法register注册到Selector对象上
*/
public class TCPServer {
public static void main(String[] args) throws IOException, InterruptedException {
//1.创建3个ServerSocketChannel对象
ServerSocketChannel channel1 = ServerSocketChannel.open();
ServerSocketChannel channel2 = ServerSocketChannel.open();
ServerSocketChannel channel3 = ServerSocketChannel.open();
//2.分别给3个ServerSocketChannel对象绑定不同的端口号
channel1.bind(new InetSocketAddress(7777));
channel2.bind(new InetSocketAddress(8888));
channel3.bind(new InetSocketAddress(9999));
//3.设置3个ServerSocketChannel对象为非阻塞
channel1.configureBlocking(false);
channel2.configureBlocking(false);
channel3.configureBlocking(false);
//4.获取Selector对象
Selector selector = Selector.open();
//5.使用3个ServerSocketChannel对象中的方法register注册到Selector对象上
channel1.register(selector, SelectionKey.OP_ACCEPT);
channel2.register(selector, SelectionKey.OP_ACCEPT);
channel3.register(selector, SelectionKey.OP_ACCEPT);
//循环接收客户端的连接
while (true){
System.out.println("服务器等待客户端连接...");
//使用Selector中的方法select:获取连接客户端的数量,没有客户端连接服务器,此方法将阻塞
int count = selector.select();
System.out.println("连接服务器的客户端数量是:"+count);
//使用Selector中的方法selectedKeys:获取客户端已经连接到通道(ServerSocketChannel)集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
System.out.println("已经连接到通道的数量:"+selectionKeys.size());
//处理Selector监听到的事件,遍历获取已经注册的Set,获取每一个SelectionKey对象
Iterator<SelectionKey> it = selectionKeys.iterator();
while (it.hasNext()){
SelectionKey selectionKey = it.next();
//获取SelectionKey中封装的ServerSocketChannel对象
ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
//获取此通道监听的端口号
System.out.println("当前通道监听到的端口号:"+channel.getLocalAddress());
//处理accept事件
SocketChannel socketChannel = channel.accept();
//读取SocketChannel发送的数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = socketChannel.read(buffer);
buffer.flip();//缩小limit范围
String msg = new String(buffer.array(), 0, buffer.limit());
System.out.println("服务器接收到客户端的数据是:"+msg);
//处理完SelectonKey监听到的事件,要在Set集合中溢出已经处理完的SelectionKey
it.remove();
}
//获取完每一个客户端的连接,睡眠2秒
Thread.sleep(2000);
}
}
}
/*
创建线程,分别创建客户端对象,连接不同的服务器
*/
public class TCPClient {
public static void main(String[] args) throws InterruptedException {
//启动两个线程,分别连接端口号是7777和8888的通道
new Thread(()->{
//轮询连接服务器
while (true){
try {
SocketChannel socketChannel = SocketChannel.open();
System.out.println("客户端开始连接服务器的7777端口...");
socketChannel.connect(new InetSocketAddress("localhost",7777));
System.out.println("客户始连接服务器的7777端口成功...");
//给服务器发送数据
socketChannel.write(ByteBuffer.wrap("你好服务器,我是连接7777端口号的客户端!".getBytes()));
break;//结束轮询
} catch (IOException e) {
System.out.println("客户端重新连接7777端口...");
System.out.println("----------------7777-----------------");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}).start();
new Thread(()->{
//轮询连接服务器
while (true){
try {
SocketChannel socketChannel = SocketChannel.open();
System.out.println("客户端开始连接服务器的8888端口...");
socketChannel.connect(new InetSocketAddress("localhost",8888));
System.out.println("客户始连接服务器的8888端口成功...");
//给服务器发送数据
socketChannel.write(ByteBuffer.wrap("你好服务器,我是连接8888端口号的客户端!".getBytes()));
break;//结束轮询
} catch (IOException e) {
System.out.println("客户端重新连接8888端口...");
System.out.println("----------------7777-----------------");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}).start();
Thread.sleep(10000);//等待服务器读取完数据
}
}
NIO2—AIO(异步,非阻塞)
AIO概述
JDK7新增的:AsynchronousIO:异步、非阻塞IO
AIO异步非阻塞连接
/*
创建AIO的服务器端:
相关的类:
java.nio.channels.AsynchronousServerSocketChannel:用于面向流的侦听套接字的异步通道。
获取对象的方法:
static AsynchronousServerSocketChannel open() 打开异步服务器套接字通道。
成员方法:
void accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler)
参数:
A attachment:附件,可以传递null
CompletionHandler handler:事件处理类,用于处理accept方法监听到的事件
CompletionHandler也叫回调函数,客户端请求服务器之后,会自动执行CompletionHandler接口中的方法
CompletionHandler接口中的方法:
void completed(V result, A attachment);客户端连接成功执行的方法
void failed(Throwable exc, A attachment);客户端连接失败执行的方法
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//创建异步非阻塞服务器AsynchronousServerSocketChannel对象
AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open();
//给AsynchronousServerSocketChannel对象绑定指定的端口号
channel.bind(new InetSocketAddress(8888));
System.out.println("accept方法开始执行....");
//使用AsynchronousServerSocketChannel对象中的方法accept监听请求的客户端对象
channel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
//客户端连接成功执行的方法
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
System.out.println("有客户端连接成功!");
}
//客户端连接失败执行的方法
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("有客户端连接失败!");
}
});
System.out.println("accept方法执行结束....");
while (true){}
}
}
/*
创建AIO的客户端:
和客户端想关的类:
java.nio.channels.AsynchronousSocketChannel:用于面向流的连接插座的异步通道。
获取对象的方法:
static AsynchronousSocketChannel open() 打开异步套接字通道。
成员方法:
abstract Future<Void> connect(SocketAddress remote) 连接服务器的方法,参数传递服务器的ip地址和端口号
使用步骤:
1.创建异步非阻塞AsynchronousSocketChannel客户端
2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建异步非阻塞AsynchronousSocketChannel客户端
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
//2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
}
}
AIO异步连接:同步非阻塞读写
public class TCPServer {
public static void main(String[] args) throws IOException {
//创建异步非阻塞服务器AsynchronousServerSocketChannel对象
AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open();
//给AsynchronousServerSocketChannel对象绑定指定的端口号
channel.bind(new InetSocketAddress(8888));
System.out.println("accept方法开始执行....");
//使用AsynchronousServerSocketChannel对象中的方法accept监听请求的客户端对象
channel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
//客户端连接成功执行的方法
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
System.out.println("有客户端连接成功!");
//获取客户端中发送的数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> future = result.read(buffer);//同步非阻塞的read方法
Integer len = null;
try {
len = future.get();//读取客户端中发送的信息
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
String msg = new String(buffer.array(),0,len);
System.out.println("服务器读取到客户端的信息:"+msg);
}
//客户端连接失败执行的方法
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("有客户端连接失败!");
}
});
System.out.println("accept方法执行结束....");
while (true){}
}
}
/*
创建AIO的客户端:
和客户端相关的类:
java.nio.channels.AsynchronousSocketChannel:用于面向流的连接插座的异步通道。
获取对象的方法:
static AsynchronousSocketChannel open() 打开异步套接字通道。
成员方法:
Future<Void> connect(SocketAddress remote) 连接此频道。
Future<Integer> read(ByteBuffer dst) 从该通道读取到给定缓冲区的字节序列。
Future<Integer> write(ByteBuffer src) 从给定的缓冲区向该通道写入一个字节序列。
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//创建异步非阻塞客户端AsynchronousSocketChannel对象
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
//使用AsynchronousSocketChannel对象中的方法connect连接服务器
socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
//连接服务器成功,给服务器发送数据
ByteBuffer buffer = ByteBuffer.wrap("你好服务器".getBytes());
socketChannel.write(buffer);
}
}
AIO异步连接:异步非阻塞读写
/*
创建AIO(异步,非阻塞)的服务器端:
相关的类:
是JDK1.7之后出现的
java.nio.channels.AsynchronousServerSocketChannel:用于面向流的侦听套接字的异步通道。
获取对象的方法:
static AsynchronousServerSocketChannel open() 打开异步服务器套接字通道。
成员方法:
AsynchronousServerSocketChannel bind(SocketAddress local) 给服务器绑定指定的端口号
void accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler) 接受连接。
参数:
A attachment:附件,可以传递null
CompletionHandler handler:事件处理相关的类,用于处理accept方法监听到的事件
CompletionHandler也叫回调函数,客户端请求服务器之后,会自动执行CompletionHandler接口中的方法
CompletionHandler接口中的常用方法:用于消除异步I / O操作结果的处理程序。
void completed(V result, A attachment) 客户端连接成功执行的方法
void failed(Throwable exc, A attachment) 客户端连接失败执行的方法
实现步骤:
1.创建异步非阻塞AsynchronousServerSocketChannel服务器对象
2.使用AsynchronousServerSocketChannel对象中的方法bind绑定指定的端口号
3.使用AsynchronousServerSocketChannel对象中的方法accept监听客户端请求
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建异步非阻塞AsynchronousServerSocketChannel服务器对象
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
//2.使用AsynchronousServerSocketChannel对象中的方法bind绑定指定的端口号
serverSocketChannel.bind(new InetSocketAddress(8888));
//3.使用AsynchronousServerSocketChannel对象中的方法accept监听客户端请求
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
//客户端连接成功执行的方法
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
System.out.println("有客户端连接成功!");
//获取客户端发送的数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
//Future<Integer> future = result.read(buffer);//是一个阻塞的方法,没有读取到客户端发送的数据,会阻塞
/*
void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer,? super A> handler) 是一个非阻塞的方法
参数
ByteBuffer dst - 要传输字节的缓冲区
long timeout - 完成I / O操作的最长时间
TimeUnit unit - timeout参数的时间单位
attachment - 要附加到I / O操作的对象; 可以是null
CompletionHandler handler - 消费结果的处理程序,回调函数
客户端给服务器发送数据之后,就会执行回调函数中的方法
*/
result.read(buffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
System.out.println("读取客户端发送数据成功,执行的方法");
buffer.flip();//缩小limit的范围
String msg = new String(buffer.array(), 0, buffer.limit());
System.out.println("服务器读取到客户端发送的信息:"+msg);
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读取客户端发送数据失败,执行的方法");
}
});
System.out.println("readu读取数据的方法执行完毕!");
}
//客户端连接失败执行的方法
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("有客户端连接失败!");
}
});
System.out.println("accept方法执行结束...");
while (true){}
}
}
/*
创建AIO的客户端:
和客户端想关的类:
java.nio.channels.AsynchronousSocketChannel:用于面向流的连接插座的异步通道。
获取对象的方法:
static AsynchronousSocketChannel open() 打开异步套接字通道。
成员方法:
abstract Future<Void> connect(SocketAddress remote) 连接服务器的方法,参数传递服务器的ip地址和端口号
Future<Integer> read(ByteBuffer dst) 从该通道读取到给定缓冲区的字节序列
Future<Integer> write(ByteBuffer src) 从给定的缓冲区向该通道写入一个字节序列。
使用步骤:
1.创建异步非阻塞AsynchronousSocketChannel客户端
2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
*/
public class TCPClient {
public static void main(String[] args) throws IOException, InterruptedException {
//1.创建异步非阻塞AsynchronousSocketChannel客户端
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
//2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
//连接服务器成功,给服务器发送数据
Thread.sleep(1000*11);
ByteBuffer buffer = ByteBuffer.wrap("你好服务器".getBytes());
socketChannel.write(buffer);
}
}