`

IO系列文章之四:一个Java NIO Server实例

 
阅读更多

本文主要通过一个非常简单的Server实例总结一下基于Java非阻塞IO的网络编程。

希望对大家有所帮助,欢迎拍砖!大笑

一、Server端代码:

该示例主要实现一个简单功能,server端直接打印客户端发送的数据。

由于例子非常简单,一个线程循环处理所有任务,算是一个最简单的NIO Server吧(实际应用开发中是不会采用这种方式的)。

MyNIOServer.java:

public class MyNIOServer {
	
	private static final int TIMEOUT = 30000;
	private static final int BUFSIZE = 10;
	private Selector selector;
	
	public MyNIOServer(int port) throws Exception{
		System.out.println("server start on port:"+port);
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.configureBlocking(false);
		ServerSocket serverSocket = serverSocketChannel.socket();
		serverSocket.bind(new InetSocketAddress(port));
		selector = Selector.open();
		serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
	}
	
	public void listen() throws Exception{
		System.out.println("server listen!");
		MyNIOServerHandler handler = new MyNIOServerHandler(BUFSIZE);
		while(true){
			System.out.println("listen while!");
			if (selector.select(TIMEOUT) == 0) {
		            System.out.print(".");
		            continue;
		        }
		       Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
			while(keyIter.hasNext()){
				SelectionKey key = keyIter.next();
				if(key.isAcceptable()){
					System.out.println("isAcceptable!");
					handler.handleAccept(key);
				}else if(key.isReadable()){
					System.out.println("isReadable!");
					handler.handleRead(key);
				}else if(key.isValid() && key.isWritable()){
					System.out.println("isWritable!");
					handler.handleWrite(key);
				}
				keyIter.remove();
			}
		}
	}
	
	public static void main(String[] args){
		try{
			MyNIOServer server=new MyNIOServer(9009);
			server.listen();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

}

处理器MyNIOServerHandler.java:

public class MyNIOServerHandler {
	
	private int BUFSIZE;
	
	public MyNIOServerHandler(int bufferSize){
		this.BUFSIZE = bufferSize;
	}
	
	public void handleAccept(SelectionKey key) throws IOException{
		System.out.println("handleAccept...");
		ServerSocketChannel serverChannel = (ServerSocketChannel)key.channel();
		SocketChannel clientChannel = serverChannel.accept();
		clientChannel.configureBlocking(false);
		Selector selector = key.selector();
		clientChannel.register(selector, SelectionKey.OP_READ,ByteBuffer.allocate(BUFSIZE));
	}
	
	public void handleRead(SelectionKey key) throws IOException{
		System.out.println("handleRead...");
		SocketChannel clientChannel = (SocketChannel)key.channel();
		ByteBuffer buf = (ByteBuffer) key.attachment();
		long bytesRead = clientChannel.read(buf);
		System.out.println("bytesRead:"+bytesRead);
		if(bytesRead==-1){
			System.out.println("clientChannel close!");
			clientChannel.close();
		}else if (bytesRead > 0) {
			String receiveText = new String(buf.array(),0,new Long(bytesRead).intValue());
			System.out.println("服务器端接受客户端数据--:"+receiveText);
			key.interestOps( SelectionKey.OP_WRITE);
		}
	}
	
	public void handleWrite(SelectionKey key) throws IOException{
	    System.out.println("handleWrite...");
	    ByteBuffer buf = (ByteBuffer) key.attachment();
	    buf.flip(); 
	    SocketChannel clntChan = (SocketChannel) key.channel();
	    clntChan.write(buf);
	    if (!buf.hasRemaining()) { 
	      key.interestOps(SelectionKey.OP_READ);
	    }
	    buf.compact(); 
	}
}

代码注解

服务器端创建一个选择器,将其与每个侦听客户端连接的套接字所对应的ServerSocketChannel注册在一起,然后反复循环,调用select()方法,并调用相应的操作器对各种类型的IO操作进行处理。

1、创建一个Selector选择器。

2、创建ServerSocketChannel实例,获得底层的ServerSocket,并以端口号作为参数绑定bind()。

3、设置信道为非阻塞模式,只有非阻塞信道才可以注册选择器。

4、为信道注册选择器,指出该信道可以进行accept操作。

5、反复轮询,等待IO,select()方法将阻塞等待,直到有准备好IO操作的信道,或者直到超时。

6、调用selectedKeys()方法,返回一个Set实例,并从中获取一个Iterator。该集合中包含了每个准备好某一IO操作的信道的SelectionKey(注册时创建)。

7、对于每个键,检查是否准备好accept()操作,是否可读或可写。

8、select()操作只是向selector所关联的键集合中添加元素。因此,不移除处理过的键,下次调用select时仍保留在集合中。

9、channel方法返回注册时用来创建键的channel,即ServerSocketChannel,这是我们注册的唯一一种支持accept操作的信道。accept为传入的连接返回一个SocketChannel实例。

10、可以通过SelectionKey方法获取相应的Selector,当SocketChannel信道准备好读数据的IO操作时,可以通过选出的键集对其进行访问。

12、handleRead,根据其支持数据读取操作可知,这是一个SocketChannel。

13、如果read方法返回-1,则表示底层连接已经关闭,此时需要关闭信道。关闭信道时,将从选择器的各种集合中移除与该信道关联的键。读取完数据将信道标记为可写。

14、handleWrite,如果缓冲区之前接收的数据已经没有剩余,则修改键关联的操作集,指示其只能进行读操作。

 

二、Client端代码

MyNIOClient.java

            String server = "localhost"; 
	    byte[] datas = "1234567890abcdef".getBytes();
	    
	    int servPort = 9009;
	    SocketChannel clntChan = SocketChannel.open();
	    clntChan.configureBlocking(false);
	    
	    if (!clntChan.connect(new InetSocketAddress(server, servPort))){
	        while (!clntChan.finishConnect()){
	          System.out.print(".");
	        }
	    }
	    
	    ByteBuffer writeBuf = ByteBuffer.wrap(datas);
	    ByteBuffer readBuf = ByteBuffer.allocate(datas.length);
	    int totalBytesRcvd = 0; 
	    int bytesRcvd;
	    while(totalBytesRcvd < datas.length){
	      if(writeBuf.hasRemaining()){
	        clntChan.write(writeBuf);
	      }
	      if ((bytesRcvd = clntChan.read(readBuf)) == -1){
	        throw new SocketException("Connection closed prematurely");
	      }
	      totalBytesRcvd += bytesRcvd;
	      System.out.print(".");
	    }
	    
	    System.out.println("Received:"+new String(readBuf.array(),0,totalBytesRcvd));
	    clntChan.close();

代码注解:

1、该套接字是非阻塞式的,因此对connect方法的调用可能会在连接建立之前返回。如果在返回前已经成功建立了连接,返回true,否则返回false。返回false时,任何发送或接受数据都将抛出异常。因此通过调用finishConnect方法轮询连接状态。不过这种忙等非常浪费系统资源,此处只是举例。

2、分别采用包装byte数组和allocate方法创建要用来读写数据的bytebuffer实例。

3、反复循环直到发送和接收完所有字节,只要输出缓冲区还留有数据,就调动write方法,对read方法的调用不会阻塞等待,但当没有数据可读时返回0.

4、打印接收到的数据,然后在信道完成其任务后也需要关闭。

 

分享到:
评论
1 楼 yuanliangding 2016-08-20  
感谢,看到nio的demo啦。

相关推荐

    Ioserver java Nio socket 框架

    Ioserver java Nio socket 框架 是个不错的NIO 通讯框架,本来想学习mina框架,看了看mina的源码太头痛,本人觉得看懂了Ioserver 再看mina的框架,想多的学习 java NIO 的也可以下载 看看,很值得学习啊!!!

    JavaNIO chm帮助文档

    Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...

    java NIO 视频教程

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...

    NioServer.java

    NioServer.java

    java nio im(server+client)

    java基于nio的通信实例,带UML结构图及server、client源码。

    java nio中文版

    java NIO是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下: – 为所有的原始类型提供 (Buffer) 缓存支持。 – 字符集编码解码解决方案。 – Channel :一个新的原始 I/O 抽象。 – 支持...

    java NIO和java并发编程的书籍

    java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...

    SimpleSocketServer:Java IO | NIO样本

    SimpleSocketServer Java IO | NIO样本使用Java IO和NIO设置自己的Socket Server(待办事项) 深入了解Tomcat和Jetty的实现方式(希望如此)

    java io 与java nio区别

    java频道\java io 与java nio区别.txt

    JavaNIO服务器实例Java开发Java经验技巧共6页

    JavaNIO服务器实例Java开发Java经验技巧共6页.pdf.zip

    Java IO_NIO

    1、Java IO_NIO 2、Java+IO.pdf

    java NIO推送实例

    java NIO 消息推送实例代码,解压Tmp.zip Desk为桌面程序,DeskAppServer为服务端程序,江巅

    java NIO 中文版

    讲解了 JavaIO 与 JAVA NIO区别,JAVA NIO设计理念,以及JDK中java NIO中语法的使用

    javaNIO实例

    该资源包含了一个用javaNIO实现的读写文件以及复制文件的简单的demo,程序注释清晰,简单易懂,喜欢的下载!!!

    java NIO实例

    实例介绍了一个简单的nio实例,适合刚接触nio的童鞋们....

    Java NIO系列教程(一) Java NIO 概述

    Java NIO系列教程(一) Java NIO 概述

    Java IO NIO and NIO 2 无水印pdf

    Java IO NIO and NIO 2 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn...

    Java NIO实例

    nio代码实例,Java NIO 系列教程,买不了吃亏,买不了上当

    一个java NIO的例子

    一个java NIO的例子 有很详细的每一步的描述,很好去理解

    Java NIO 中文 Java NIO 中文 Java NIO 中文文档

    Java NIO 深入探讨了 1.4 版的 I/O 新特性,并告诉您如何使用这些特性来极大地提升您所写的 Java 代码的执行效率。这本小册子就程序员所面临的有代表性的 I/O 问题作了详尽阐述,并讲解了 如何才能充分利用新的 I/O ...

Global site tag (gtag.js) - Google Analytics