Why java nio_Java NIO示例

这是一个使用Java NIO实现的TCP客户端和服务器端的示例代码。客户端通过非阻塞模式连接服务器,并持续读取用户输入,当接收到'quit'时断开连接。服务器端监听指定端口,接受新连接并处理来自客户端的读事件,读取数据后回应消息。
摘要由CSDN通过智能技术生成

Client端

/**

* 《构建高性能的大型分布式Java应用》书中的示例代码, 版权所有:2008---2009

*/

package book.chapter1.tcpnio;

import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.net.InetSocketAddress;

import java.net.SocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.nio.charset.Charset;

/**

* 描述:基于java NIO实现的tcp client

*

* @author bluedavy

* 创建时间: 2008-12-2

*/

public class Client {

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

Charset charset = Charset.forName("UTF-8");

int port = 9527;

SocketChannel socketChannel = SocketChannel.open();

socketChannel.configureBlocking(false); // 设置为非阻塞模式

SocketAddress target =new InetSocketAddress("127.0.0.1", port);

socketChannel.connect(target); // 对于非阻塞模式,立即返回false,表示连接正在建立中

Selector selector = Selector.open();

socketChannel.register(selector, SelectionKey.OP_CONNECT); // 向register注册该channel及其感兴趣的连接事件

BufferedReader systemIn = new BufferedReader(new InputStreamReader(System.in));

while(true){ // 这里体现了NIO需要不停地去检测是否有感兴趣的事件到达这个特点,是与AIO最大的区别之一

if(socketChannel.isConnected()){

String command = systemIn.readLine();

socketChannel.write(charset.encode(command));

if(command==null || "quit".equalsIgnoreCase(command.trim())){

systemIn.close();

socketChannel.close();

selector.close();

System.out.println("Client quit!");

System.exit(0);

}

}

// 阻塞至有兴趣的IO事件发生,或达到超时(1000ms)时间,如果希望一直等至有兴趣的IO事件发生,可调用

// 无参数的select()方法,如果希望不阻塞直接返回目前是否有感兴趣的事件发生,可调用selectNow()方法

int nKeys = selector.select(1000);// 超时时间:1000ms

if (nKeys>0) {// select方法返回的nKeys是感兴趣的事件的个数

for (SelectionKey key : selector.selectedKeys()) {

if (key.isConnectable()) {// 可连接事件

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

sc.configureBlocking(false);

// 注册感兴趣的IO读事件,通常不直接注册写事件,在socket发送缓冲区未满的情况下,一直是

// 可写的。如果注册了写事件,而又不用写数据,很容易造成程序空转,CPU消耗100%的现象。

sc.register(selector, SelectionKey.OP_READ);

// 查看SocketChanel.connect(SocketAddress)方法注释可知,finishConnect方法

// 必须被调用,否则连接没有建立完成,注册的事件不会生效

sc.finishConnect();

} else if (key.isReadable()) {//可读事件,socket读缓冲区有数据到达

ByteBuffer buffer=ByteBuffer.allocate(1024);

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

int readBytes=0;

try{

int ret=0;

try{

// 读取目前可读的数据,sc.read(buffer)返回成功复制到buffer中的字节数,

// 此步骤为同步-非阻塞操作,这里体现出了NIO是【同步-非阻塞】IO模式,

// 返回值可能为0,且当已经是流的结尾(Socket已关闭)时返回-1

while((ret=sc.read(buffer))>0){

readBytes+=ret;

}

} finally {

buffer.flip(); // ready for read from buffer.

}

if(readBytes>0){

System.out.println(charset.decode(buffer).toString());

buffer = null;

}

} finally {

if(buffer!=null){

buffer.clear();

}

}

} else if (key.isWritable()) { // 可写事件,socket发送缓冲区有可用空间

// 首先要取消对写事件的注册

key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));

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

// 此步骤为同步操作,这里也体现出了NIO是【同步-非阻塞】IO模式,同步直到写入socket发送

// 缓冲区或网络IO出现异常,返回值为成功写入的字节数,当socket发送缓冲区已满时,此处返回0

int writtenSize = sc.write(someByteBuffer);

// 如写入字节数为0,说明socket发送缓冲区已满,此时正是需要注册写事件的时刻

// 其他需要进行写操作的地方,推荐的做法也是直接写入,如果写入返回0,再注册可写事件

if (writtenSize == 0) {

key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);

}

}

}

// 到此为止,已处理完本次检测出的感兴趣事件,所以需要清理待处理事件集

selector.selectedKeys().clear();

}

}

}

}

Server端

/**

* 《构建高性能的大型分布式Java应用》书中的示例代码, 版权所有:2008---2009

*/

package book.chapter1.tcpnio;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

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.nio.charset.Charset;

/**

* 描述:基于Java NIO实现的tcp服务器端

*

* @author bluedavy

* 创建时间: 2008-12-2

*/

public class Server {

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

int port = 9527;

Selector selector = Selector.open();

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

ServerSocket serverSocket = serverSocketChannel.socket();

serverSocket.bind(new InetSocketAddress(port));

System.out.println("Server listen on port: "+port);

serverSocketChannel.configureBlocking(false);

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册监听新连接到达事件

while (true) {

int nKeys=selector.select(1000);

if (nKeys>0) {

for (SelectionKey key : selector.selectedKeys()) {

if (key.isAcceptable()) { // ServerSocketChannel的ACCEPT事件

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

SocketChannel sc = serverSocketChannel.accept();

if (sc==null) {

continue;

}

sc.configureBlocking(false);

// 这里SocketChannel和ServerSocketChannel共用了同一个Selector对象

sc.register(selector, SelectionKey.OP_READ); // SocketChannel注册可读事件

} else if (key.isReadable()) { // SocketChannel的READ事件

ByteBuffer buffer = ByteBuffer.allocate(1024);

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

int readBytes = 0;

String message = null;

try{

int ret;

try{

while ((ret=sc.read(buffer)) > 0) { // 一直读,直到buffer读SocketChannel中没有数据或者buffer中没有可用空间,就返回0

readBytes+=ret;

}

} catch (Exception e) {

readBytes=0;

// IGNORE

} finally {

buffer.flip();

}

if (readBytes > 0) {

message=Charset.forName("UTF-8").decode(buffer).toString(); // Charset.forName("UTF-8") 获取到的Charset是单例的

buffer = null; //释放buffer

}

} finally {

if (buffer != null) {

buffer.clear();

}

}

if (readBytes > 0) {

System.out.println("Message from client: "+ message);

if ("quit".equalsIgnoreCase(message.trim())) {

sc.close();

selector.close();

System.out.println("Server has been shutdown!");

System.exit(0);

}

String outMessage="Server response:"+message;

sc.write(Charset.forName("UTF-8").encode(outMessage));

}

}

}

// 清除已处理过的事件

selector.selectedKeys().clear();

}

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值