本编文章意在循环渐进,可看最后一个就可以了
文章来源:https://www.toymoban.com/news/detail-410041.html
Selector简介
【1】创建Selector
Selector selector = Selector.open();
【2】channel注册到Selector
首先channel必须是非阻塞的情况下
channel.register(选择器,操作的类型,绑定的组件);返回的是选择键
【3】轮询查询就绪操作
【4】停止选择的方法
NIO 编程步骤
代码1.0
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
//创建selector选择器
Selector selector = Selector.open();
//将ssc注册到选器器(建立两者的联系)
SelectionKey sscKey = ssc.register(selector, 0, null);
//选择哪种监听的事件
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果没有事件发生,线程将在此处停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能发生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
System.out.println("key::"+key);
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//获取相对应的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已经在selector中注册了!");
}
}
}
}
结果分析:
解决方案
【1】区分触发seletor.select()的事件
【2】处理完一个事件,在对应的注册的keys集合删除。
代码2.0
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果没有事件发生,线程将在此处停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能发生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();//解决处理后事件在set集合中还有的现象
if (key.isAcceptable()){//区分不同事件触发的结果
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//获取相对应的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已经在selector中注册了!");
}else if (key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(32);
int len = channel.read(buffer);
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer).toString());
buffer.clear();
}
}
}
}
}
新的问题 如果ByteBuffer分配的空间不够用会出现什么结果(分析)
当一次没有读完后就会触发下一次的读取,
解决方案:为每一个连接客户端的channel的ByteBuffer的空间动态扩容,(可以避免黏包半包的情况!)
新的问题:
当客户端强制关机,服务器停止
当客户端正常结束,服务器进入无限循环
解决方案:
客户端强制关机,服务器报异常,使用try--catch语句 key.cancel()对此不做任何事情
客户端正常结束,客户端向服务器发送数据,read = -1;
代码3.0(最终篇)
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果没有事件发生,线程将在此处停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能发生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();//解决处理后事件在set集合中还有的现象
if (key.isAcceptable()){//区分不同事件触发的结果
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//获取相对应的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
ByteBuffer buffer = ByteBuffer.allocate(4);
scKey.attach(buffer);//为每一个注册到set集合中的channel分配独立的缓冲区
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已经在selector中注册了!");
}else if (key.isReadable()){
try {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
int read = channel.read(buffer);
// System.out.println("read::"+read);
// System.out.println("positon:"+buffer.position()+"limit:"+buffer.limit());
System.out.println(buffer);
if (read == -1){//客户端正常结束,read的值等于-1
key.cancel();
continue;
}
if(read == buffer.capacity()){
ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity()*2);
newBuffer.flip();
newBuffer.put(buffer);
key.attach(newBuffer);
}else{
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer).toString());
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
key.cancel();
}
}
}
}
}
}
结果:
客户端正常结束
客户端强制结束
Selector写事件与读事件类似
服务器
public class SelectorWriter {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT, null);
while (true){
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()){
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
SelectionKey sckey = sc.register(selector, SelectionKey.OP_READ);
//发送大量的数据
StringBuilder str = new StringBuilder();
for (int i = 0; i < 300000; i++) {
str.append("a");
}
ByteBuffer buffer = Charset.defaultCharset().encode(str.toString());
int write = sc.write(buffer);
System.out.println(write);
if (buffer.hasRemaining()){
// 4. 关注可写事件
sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);
// sckey.interestOps(sckey.interestOps() | SelectionKey.OP_WRITE);
// 5. 把未写完的数据挂到 sckey 上
sckey.attach(buffer);
}
}else if(key.isWritable()){
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
int write = sc.write(buffer);
System.out.println(write);
//清理工作
if (!buffer.hasRemaining()){
key.attach(null);
key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
}
}
}
}
}
}
客户端
public class WriterClient {
public static void main(String[] args) throws Exception {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8080));
// 3. 接收数据
int count = 0;
while (true) {
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
count += sc.read(buffer);
System.out.println(count);
buffer.clear();
}
}
}
文章来源地址https://www.toymoban.com/news/detail-410041.html
到了这里,关于Java 网络编程之NIO(selector)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!