Java NIO示例

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 channel=SocketChannel.open();
		channel.configureBlocking(false);//设置为非阻塞模式
		SocketAddress target=new InetSocketAddress("127.0.0.1",port);
		channel.connect(target);//对于非阻塞模式,立即返回false,表示连接正在建立中
		Selector selector=Selector.open();        
		channel.register(selector, SelectionKey.OP_CONNECT);//向register注册该channel及其感兴趣的连接事件
		BufferedReader systemIn=new BufferedReader(new InputStreamReader(System.in));
		while(true){//这里体现了NIO需要不停地去检测是否有感兴趣的事件到达这个特点,是与AIO最大的区别之一
			if(channel.isConnected()){
				String command=systemIn.readLine();
				channel.write(charset.encode(command));
				if(command==null || "quit".equalsIgnoreCase(command.trim())){
					systemIn.close();
					channel.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 ssc=ServerSocketChannel.open();
		ServerSocket serverSocket=ssc.socket();
		serverSocket.bind(new InetSocketAddress(port));
		System.out.println("Server listen on port: "+port);
		ssc.configureBlocking(false);
		ssc.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 server=(ServerSocketChannel) key.channel();
						SocketChannel sc=server.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){
									readBytes+=ret;
								}
							}
							catch(Exception e){
								readBytes=0;
								// IGNORE
							}
							finally{
								buffer.flip();
							}
							if(readBytes>0){
								message=Charset.forName("UTF-8").decode(buffer).toString();
								buffer = null;
							}
						}
						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();
			}
		}
	}
	
}

 

转载于:https://my.oschina.net/mojiewhy/blog/3003654

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值