java nio 客户端关闭_NIO 服务器客户端关闭,循环读问题

本文通过一个Java NIO服务器示例,展示了在客户端正常或异常关闭连接时,如何处理服务器的循环读问题。当客户端断开连接时,服务器如果没有正确检查读取状态,可能导致CPU占用过高。解决方案是在读取通道数据时,检查读取的字节数,如果为负值表示通道已关闭,及时关闭通道并取消选择键。
摘要由CSDN通过智能技术生成

如此代码,服务器端读取客户端信息时,如果客户端正常关闭,出现循环读问题,CPU被占满。

package com.timer.netty.select;

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;

/**

* Created by zhubo on 2018/1/16.

*/

public class NIOServer {

private Selector selector;

public void initServer(int port) throws Exception{

//获得一个ServerSocket通道

ServerSocketChannel serverChannel = ServerSocketChannel.open();

//设置通道为非阻塞

serverChannel.configureBlocking(false);

// 该通道对于serverSocket绑定到port端口

serverChannel.socket().bind(new InetSocketAddress(port));

// 获得一选择器

this.selector = Selector.open();

//将通道管理器和该通道绑定,并未该通道注册selectionKey.OP_ACCEPT事件

//注册该事件后,当事件到达的时候,selector.select()会返回,

// 如果事件没有到达selector.select()会一直阻塞

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

}

public void listen() throws Exception{

System.out.println("start server ...");

// 轮训访问seletor

while(true){

//当注册事件到达时,方法返回,否则该方法会一直阻塞

selector.select();

// 获得selector中选中项的地带器,选中项为注册事件

Iterator iterator = this.selector.selectedKeys().iterator();

if(iterator.hasNext()){

SelectionKey key = iterator.next();

//删除已选的key 以防止重复处理

iterator.remove();

//客户端请求链接事件

if(key.isValid() && key.isAcceptable()){

System.out.println("acceptable....");

ServerSocketChannel server = (ServerSocketChannel) key.channel();

//获取客户端链接的通道

SocketChannel channel = server.accept();

// 非阻塞模式

channel.configureBlocking(false);

//channel.write(ByteBuffer.wrap(new String("Hello Client").getBytes()));

// 在客户端链接成功后,为了可以连接到客户端的信息,需要给通道设置读的权限

channel.register(this.selector,SelectionKey.OP_READ);

}else if(key.isValid() && key.isReadable()){

read(key);

}

}

}

}

private void read(SelectionKey key) throws Exception {

SocketChannel channel = (SocketChannel)key.channel();

ByteBuffer buffer = ByteBuffer.allocate(100);

channel.read(buffer);

byte[] data = buffer.array();

String msg = new String(data).trim();

System.out.println("server receive from client : "+msg);

ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());

channel.write(outBuffer);

//key.cancel();

//channel.close();

}

public static void main(String[] args) throws Exception{

NIOServer server = new NIOServer();

server.initServer(8989);

server.listen();

}

}

[root@centos1 ~]# telnet 192.168.1.114 8989

Trying 192.168.1.114...

Connected to 192.168.1.114.

Escape character is '^]'.

^]

telnet> send ao

锟斤拷

kdf

kdfdd

ddquit

quit^]

telnet> quit

Connection closed.

[root@centos1 ~]# telnet 192.168.1.114 8989

Trying 192.168.1.114...

Connected to 192.168.1.114.

Escape character is '^]'.

^]

telnet> send ao

锟斤拷

abc

abcdd

dd^]

telnet> quit

Connection closed.

[root@centos1 ~]#

当我们telnet切断客户端链接时,会一直出发SocketChannel读就绪事件。会出现循环度问题。

需要我们手动对读取通道数据代码进行改写:

private void read(SelectionKey key) throws Exception {

SocketChannel channel = (SocketChannel)key.channel();

ByteBuffer buffer = ByteBuffer.allocate(100);

try{

int read = channel.read(buffer);

if(read > 0){

byte[] data = buffer.array();

String msg = new String(data).trim();

System.out.println("server receive from client : "+msg);

ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());

channel.write(outBuffer);

}else if(read < 0){

System.out.println("channel closed");

channel.close();

key.cancel();

}else

;

}catch (Exception e){

System.out.println("channel exception closed");

channel.close();

key.cancel();

}

//key.cancel();

//channel.close();

}

这样不管,客户端异常关闭导致的服务器异常退出,还是 客户端正常关闭导致的循环读问题都不会出现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值