个人总结:
阻塞式编程:客户端向服务器端建立通道,发送数据时,由于服务器端不知道数据什么时候发送完毕或者无法验证数据的真假时,造成阻塞,可用多线程方式降低阻塞。
非阻塞式编程:当客户端向服务器端建立通道时,需要在选择器上进行通道的注册,选择器的功能是监听已注册的通道的某种任务的状态(包含多种状态:读、写、连接、接受数据),当其中一种状态完全准备就绪时,选择器才会将这个任务分配到一个或多个服务器的线程上,进行操作运行
基于TCP:
package test;
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.Date;
import java.util.Iterator;
import java.util.Scanner;
import org.junit.Test;
public class TestNonBlockingNIO {
//客户端
@Test
public void client() throws IOException {
//1.打开通道
SocketChannel sChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
//2.切换为非阻塞模式
sChannel.configureBlocking(false);
//3.分配指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
//4.发送给服务器端
Scanner scan=new Scanner(System.in);
while(scan.hasNext()) {
String str=scan.next();
buf.put((new Date().toString()+"\n"+str).getBytes());
buf.flip();
sChannel.write(buf);
buf.clear();
}
//5.关闭通道
sChannel.close();
}
//服务器端
@Test
public void server() throws IOException {
//1.获取通道
ServerSocketChannel ssChannel=ServerSocketChannel.open();
//2.切换为非阻塞模式
ssChannel.configureBlocking(false);
//3.绑定连接
ssChannel.bind(new InetSocketAddress(9898));
//4.获取选择器
Selector selector=Selector.open();
//5.将通道注册到选择器上,并且指定“监听接受状态(事件)”
//什么时候接受状态准备就绪了,再去获取连接
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//6.轮询式的获取选择器上已经“准备就绪”的事件
while(selector.select()>0) {//当selector.select()>0表示已有事件就绪
//7.获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
Iterator<SelectionKey> it=selector.selectedKeys().iterator();//selectedKeys()集合中包含了所有监听的事件
while(it.hasNext()) {
//8.获取准备“就绪”的事件
SelectionKey sk=it.next();
//9.判断具体是什么事件准备就绪
if(sk.isAcceptable()) {
//10.若“接受就绪”,获取客户端连接
SocketChannel sChannel=ssChannel.accept();
//11.切换非阻塞模式
sChannel.configureBlocking(false);
//12.将该通道注册到选择器中
sChannel.register(selector, SelectionKey.OP_READ);
}else if(sk.isReadable()) {
//13.获取当前选择器上“读就绪”状态的通道
SocketChannel sChannel=(SocketChannel) sk.channel();
//14.读取数据
ByteBuffer buf=ByteBuffer.allocate(1024);
int len=0;
while((len=sChannel.read(buf))>0) {
buf.flip();
System.out.println(new String(buf.array(),0,len));
buf.clear();
}
}
//15.取消选择键SelextionKey,以保证下一次监听时,它不在就绪状态
it.remove();
}
}
}
}
先运行服务器端,再运行客户端:
基于UDP:
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import org.junit.Test;
public class TestNonBlockingNIO2 {
//发送端
@Test
public void send() throws IOException {
DatagramChannel dc=DatagramChannel.open();
dc.configureBlocking(false);
ByteBuffer buf=ByteBuffer.allocate(1024);
Scanner scan=new Scanner(System.in);
while(scan.hasNext()) {
String str=scan.next();
buf.put((new Date().toString()+":\n"+str).getBytes());
buf.flip();
dc.send(buf, new InetSocketAddress("127.0.0.1",9898));
buf.clear();
}
dc.close();
}
//接受端
@Test
public void receive() throws IOException {
DatagramChannel dc=DatagramChannel.open();
dc.configureBlocking(false);
dc.bind(new InetSocketAddress(9898));
Selector selector=Selector.open();
dc.register(selector, SelectionKey.OP_READ);
while(selector.select()>0) {
Iterator<SelectionKey> it=selector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey sk=it.next();
if(sk.isReadable()) {
ByteBuffer buf=ByteBuffer.allocate(1024);
dc.receive(buf);
buf.flip();
System.out.println(new String(buf.array(),0,buf.limit()));
buf.clear();
}
it.remove();
}
}
}
}
先运行接受端,后运行发送端: