`

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啦。

相关推荐

    JAVA-NIO程序设计完整实例

    Java NIO(New IO)是Java 1.4引入的一个新特性,它为Java提供了非阻塞I/O操作的能力,使得Java在处理I/O时更加高效。NIO与传统的BIO(Blocking I/O)模型相比,其核心在于它允许程序同时处理多个输入和输出流,提高...

    NIO与传统IO代码区别实例

    但你可以查阅Java NIO的相关文档,例如`java.nio.channels.Selector`和`java.nio.channels.ServerSocketChannel`等类,学习如何构建一个非阻塞的NIO服务器。 总的来说,理解IO与NIO的区别,以及它们在不同场景下的...

    Java NIO原理 图文分析及代码实现

    为了更好地理解Java NIO的使用方式,下面我们通过简单的代码示例来展示如何实现一个基本的NIO服务端和客户端。 **服务端代码实现** ```java package cn.nio; import java.io.IOException; import java.net....

    Java NIO原理分析及代码实例

    通道可以从一个源头(如文件或网络套接字)读取数据,并将数据写入另一个目的地。 2. **缓冲区(Buffer)**:缓冲区是存储数据的容器,它提供了对数据的更高效管理和访问。在Java NIO中,有ByteBuffer、CharBuffer...

    socket通信NIO代理模式demo实例

    NIO(Non-blocking I/O)是Java提供的一个高效I/O模型,相较于传统的IO模型,NIO具有非阻塞、多路复用等特性,能够更好地处理大量并发连接。本实例"socket通信NIO代理模式demo"将展示如何利用NIO来构建一个高性能的...

    tomcat:Java使用nio模式实现tomcat

    Tomcat是Apache软件基金会的Jakarta项目下的一个开源Web应用服务器,它是基于Java Servlet和JavaServer Pages技术的标准实现。为了更好地处理大量并发请求,Tomcat可以配置为使用NIO连接器,即`org.apache.coyote....

    java网络开发实例

    在这个“java网络开发实例”压缩包中,我们可能会发现一系列的程序代码,这些代码可以帮助我们深入理解和实践Java在网络编程中的应用。以下是其中可能涉及的一些核心知识点: 1. **Socket编程**:Java的Socket类是...

    【IT十八掌徐培成】Java基础第27天-04.NIO-Selector-Server-Client.zip

    Java NIO(New IO)是Java 1.4版本引入的一个新模块,全称为Non-blocking Input/Output,它提供了一种不同于传统IO的I/O操作方式。传统的IO模型基于阻塞IO,即当进行读写操作时,如果数据未准备好,程序会一直等待,...

    JAVA上百实例源码以及开源项目源代码

    数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax写的...

    io和newio的用法和比较 socket 实例

    NIO的一个关键特性是非阻塞I/O,允许一个线程处理多个连接,这对于服务器端高并发的Socket编程非常有利,如使用Selector实现的多路复用。 在Socket编程中,传统的I/O示例可能如下: ```java Socket socket = ...

    基于Java的实例源码-HTTP服务器 TJWS.zip

    TJWS(Tiny Java Web Server)是一个轻量级的Java Web服务器,用于演示和学习如何在Java环境中构建Web服务。 【描述解析】 描述部分 "基于Java的实例源码-HTTP服务器 TJWS.zip" 明确指出这是一个Java编程的实践项目...

    《java网络编程实例》配书光盘

    Java提供多种方式实现这一点,如使用`java.nio`包的非阻塞I/O,或者使用`java.io`的流式操作。FTP和HTTP协议也可用于文件传输,可以借助第三方库如Apache Commons Net。 6. **邮件收发**:JavaMail API是Java中处理...

    Java Socket PC端传输文件简易服务器客户端

    在高并发场景下,可以考虑使用非阻塞IO或者NIO(New IO)来提高效率。 10. **数据完整性校验**: - 在文件传输过程中,为了确保数据的完整性和一致性,通常会在文件传输前后添加一些校验信息,如CRC校验或MD5校验...

    JAVA+Socket教程

    Java Socket教程是一个深入学习Java网络编程的重要资源,它涵盖了如何使用Java的Socket API进行客户端-服务器通信的关键概念和技术。在本文中,我们将详尽探讨Java Socket编程的基础、工作原理以及如何实现基本的...

    基于Java的RTSP服务源码

    本资源提供的“基于Java的RTSP服务源码”是一个用Java实现的RTSP服务器,它允许开发者创建和管理实时多媒体流。 RTSP的主要目标是提供一个框架,使得客户端可以请求服务器上的媒体数据,并控制播放的速率、方向、...

    基于Java的分离SQL Server数据库.zip

    8. **文件操作**: 如果你想复制数据库文件,Java也提供了文件I/O操作,如`java.io.File`和`java.nio.file`包中的类,可以用来移动或复制数据库的数据和日志文件。 9. **数据库恢复**: 分离数据库后,如果需要重新...

    Server_Ftp.rar_FTP SERVER_java socket _java socket ftp_服务器_进程通信

    10. **配置与管理**:一个完整的FTP服务器还可能包含用户管理和权限控制,以及日志记录等功能,以满足实际部署和管理的需求。 在提供的压缩包中,`www.pudn.com.txt`可能是文档或示例代码,而`FtpServer`可能是FTP...

    NIO聊天室代码

    在Java编程领域,NIO(New IO)是一个重要的特性,它是Java 1.4引入的,为处理大量并发连接提供了一种高效的方式。NIO聊天室的实现是学习和理解NIO机制的一个实用示例。在这个项目中,我们将探讨如何使用NIO构建一个...

    Java 客户端服务器程序 学习笔记

    在Java编程领域,客户端-服务器(Client-Server)程序是一种常见的架构模式,它涉及两个主要组件:客户端应用程序和服务器端应用程序。在这个“Java客户端服务器程序学习笔记”中,我们将深入探讨这一主题,包括如何...

Global site tag (gtag.js) - Google Analytics