`

Java 时间服务器demo之NIO实现

    博客分类:
  • java
阅读更多

0.前文

Java 时间服务器demo之线程池

1.NIO主要类库

缓冲区Buffer

通道Channel

多路复用器Selector

 

2.Java IO与NIO比较

面向流与面向缓冲

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

3.Java IO与NIO的使用场景比较

如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。
 
如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。

 

4.时间服务器demo

代码来自 李林峰《Netty权威指南》

服务端:

TimeServer.java

 

package com.ccy.IO.nio;

import java.io.IOException;

public class TimeServer {
	public static void main(String[] args) throws IOException {
		int port = 8080;
		if(args!=null && args.length>0){
			port = Integer.valueOf(args[0]);
		}
		
		MultTimeServer server = new MultTimeServer(port);
		new Thread(server).start();
	}
}


MultTimeServer.java

 

package com.ccy.IO.nio;

import java.io.IOException;
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.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Set;



public class MultTimeServer implements Runnable{
	private Selector selector;
	private ServerSocketChannel channel;
	private volatile boolean stop;
	
	
	public MultTimeServer(int port){
		try {
			selector = Selector.open();
			channel = ServerSocketChannel.open();
			channel.configureBlocking(false);
			channel.socket().bind(new InetSocketAddress(port), 1024);
			channel.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("TIME SERVER IS LISTENING!!!");
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	
	public void stop(){
		this.stop =true;
	}

	@Override
	public void run() {
		while(!stop){
			try {
				//selector每一秒被唤醒一次
				selector.select(1000);
				//还回就绪状态的chanel的selectedKeys
				Set<SelectionKey> selectedKeys = selector.selectedKeys();
				Iterator<SelectionKey> iterator = selectedKeys.iterator();
				SelectionKey key = null;
				while(iterator.hasNext()){
					key = iterator.next();
					iterator.remove();
					try{
						handleInput(key);
					}catch (Exception e) {
						if (key != null) {
						    key.cancel();
						    if (key.channel() != null)
							key.channel().close();
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if(selector!=null){
			try {
				selector.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	
	public void handleInput(SelectionKey key) throws IOException{
		if(key.isValid()){
			if(key.isAcceptable()){
				//通过ServerSocketChannel的accept()操作接收客户端的请求并创立SocketChannel连接,相当于完成TCP三次握手操作
				ServerSocketChannel schannel = (ServerSocketChannel) key.channel();
				SocketChannel accept = schannel.accept();
				accept.configureBlocking(false);
				accept.register(selector, SelectionKey.OP_READ);
			}
			if(key.isReadable()){
				SocketChannel sc = (SocketChannel) key.channel();
				//开辟缓冲区
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				//非阻塞读
				int size = sc.read(buffer);
				//根据还回结果做判断
				if(size>0){
					//设置当前读取位置 
					buffer.flip();
					byte[] arr = new byte[buffer.remaining()];
					buffer.get(arr);
					String body = new String(arr,"UTF-8");
					System.out.println(body);
					SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
					doWriter(sc,format.format(new java.util.Date()));
				}else if(size<0){
					key.cancel();
					sc.close();
				}
			}
			
		}
	}


	private void doWriter(SocketChannel sc, String res) throws IOException {
		if(res != null && res.trim().length()>0){
			byte[] bytes = res.getBytes();
			ByteBuffer buffe = ByteBuffer.allocate(bytes.length);
			buffe.put(bytes);
			buffe.flip();
			sc.write(buffe);
		}
	}
}

客户端:

TimeClient.java

 

package com.ccy.IO.nio;

public class TimeClient {
	public static void main(String[] args) {
		int port = 8080;
		if(args!=null && args.length>0){
			port = Integer.valueOf(args[0]);
		}
		new Thread(new TimeClientHandler("127.0.0.1", port)).start();
	}
}


TimeClientHandler.java

 

package com.ccy.IO.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class TimeClientHandler implements Runnable {
	private int port;
	private String host;
	private Selector selector;
	private SocketChannel channel;
	private volatile boolean stop;
	
	public TimeClientHandler(String host,int port){
		this.host = host ==null?"127.0.0.1":host;
		this.port = port;
		try {
			selector = Selector.open();
			channel = SocketChannel.open();
			channel.configureBlocking(false);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	
	@Override
	public void run() {
		try {
			doConnect();
		} catch (IOException e1) {
			e1.printStackTrace();
			System.exit(1);
		}
		while(!stop){
			try {
				//selector每一秒被唤醒一次
				selector.select(1000);
				//还回就绪状态的chanel的selectedKeys
				Set<SelectionKey> selectedKeys = selector.selectedKeys();
				Iterator<SelectionKey> iterator = selectedKeys.iterator();
				SelectionKey key = null;
				while(iterator.hasNext()){
					key = iterator.next();
					iterator.remove();
					try{
						handleInput(key);
					}catch (Exception e) {
						if (key != null) {
						    key.cancel();
						    if (key.channel() != null)
							key.channel().close();
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if(selector!=null){
			try {
				selector.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			selector =null;
		}
	}
	
	public void handleInput(SelectionKey key) throws IOException{
		if(key.isValid()){
			SocketChannel sc = (SocketChannel) key.channel();
		    if (key.isConnectable()) {
				if (sc.finishConnect()) {
				    sc.register(selector, SelectionKey.OP_READ);
				    doWrite(sc);
				} else{
					System.exit(1);// 连接失败,进程退出
				}
		    }
			if(key.isReadable()){
				ByteBuffer readBuffer = ByteBuffer.allocate(1024);
				int readBytes = sc.read(readBuffer);
				if (readBytes > 0) {
				    readBuffer.flip();
				    byte[] bytes = new byte[readBuffer.remaining()];
				    readBuffer.get(bytes);
				    String body = new String(bytes, "UTF-8");
				    System.out.println("Now is : " + body);
				    this.stop = true;
				} else if (readBytes < 0) {
				    // 对端链路关闭
				    key.cancel();
				    sc.close();
				} else
				    ; // 读到0字节,忽略
			}
			
		}
	}

	
	private void doConnect() throws IOException{
		if(channel.connect(new InetSocketAddress(host, port))){
			channel.register(selector, SelectionKey.OP_READ);
			doWrite(channel);
		}else{
			channel.register(selector, SelectionKey.OP_CONNECT);
		}
	}

	private void doWrite(SocketChannel schannel) throws IOException {
		byte[] bytes = "What time is it now?".getBytes();
		ByteBuffer buff = ByteBuffer.allocate(bytes.length);
		buff.put(bytes);
		buff.flip();
		schannel.write(buff);
		//判断是否发送完毕
		if(!buff.hasRemaining()){
			System.out.println("SEND SUCCESS!");
		}
		
	}
	
	
	
}

5.NIO版本时间服务器分析

 

3.1 通过多路复用器selector,客户端发起的链接是异步的,无须等待

3.2 SocketChannel读写操作亦是异步操作,提高了IO

3.3 线程模型优化,Selector通过epoll实现无连接数的限制,这意味着一个Selector可以处理成千上百个链接,而且性能不会随着客户端的数量的增加而下降,非常适合做高性能,高负载的网络服务器、

6.关于NIO的更多知识

http://www.iteye.com/magazines/132-Java-NIO#585

http://ifeve.com/overview/

 

 

 

先模仿在创造吧!

更多精彩内容请继续关注我的博客:http://blog.csdn.net/caicongyang

 

记录与分享,你我共成长-fromcaicongyang

 

 

 

分享到:
评论

相关推荐

    java nio 通信服务器、客户端完整例子

    用java编写的nio通信的例子,nio是io编程的新版本,比io较流行。同时本例子是适用socket通信的。可以在此基础上,添加您的个人应用。本例子适用于:java通信的学习者,android平台通信的学习者。

    JAVA-NIO-DEMO

    本示例"JAVA-NIO-DEMO"提供了关于Java NIO的实际应用,通过Anontion(注解)、Applet(小程序)和NIO的Demo,帮助开发者更深入地理解和掌握这些概念。 首先,让我们深入了解Java NIO。NIO的核心组件包括: 1. **...

    基于java NIO的socket通信demo

    在这个“基于java NIO的socket通信demo”中,我们将探讨如何使用NIO进行服务器和客户端的Socket通信,并解决通信过程中的字符集乱码问题。 首先,我们来看`NioServer.java`。这个文件中包含了一个基于NIO的服务器端...

    nio demo for nio学习笔记(体系结构以及模块介绍)

    Java NIO库提供了多种实现,如`java.nio.channels`包下的各种Channel和Selector类,以及`java.nio`包下的Buffer类。 在学习NIO时,首先需要理解Channel、Buffer、Selector的基本概念和使用方法,然后通过实例来熟悉...

    基于NIO非阻塞的java聊天demo(支持单聊和群聊)

    在这个基于NIO非阻塞的Java聊天demo中,我们将会看到如何利用NIO实现一个支持单聊和群聊的应用。 首先,NIO的核心组件包括Channel、Buffer、Selector和Pipe。在传统的IO模型中,数据是从流的一端流向另一端,而在...

    java-NIO-demo

    在"java-NIO-demo"这个示例中,你将看到如何使用这些NIO组件来实现I/O操作。你可以安装完JDK后直接运行代码,体验NIO带来的效率提升。通常,这些示例可能包括创建通道(如SocketChannel和ServerSocketChannel),...

    bio nio aio demo

    在"bio nio aio demo"项目中,我们可以通过运行`main`方法来体验这三种模型的不同之处。这个示例可能会包含三个部分:BIO服务器、NIO服务器和AIO服务器。每部分都会展示如何创建服务器、接收客户端连接、处理请求和...

    基于netty的nio使用demo源码

    Netty是一个高性能、异步事件...这个NioDemo示例代码可以帮助我们理解Netty如何利用NIO来实现高效的网络通信。通过阅读和分析源码,我们可以深入理解Netty的事件驱动模型、NIO机制以及如何在实际项目中应用这些概念。

    BIO,NIO,AIO实现的demo

    在提供的"**BIO_TimeServer**"压缩包中,我们可以找到基于BIO实现的时间服务器示例。这个示例通常包括一个服务器端(SERVER)和一个客户端(CLIENT)。服务器端会创建一个监听套接字,等待客户端的连接请求。当...

    socket通信NIO代理模式demo实例

    本实例"socket通信NIO代理模式demo"将展示如何利用NIO来构建一个高性能的代理服务器。 代理模式是一种设计模式,它允许我们创建一个代理对象来控制对原对象的访问。在NIO代理模式中,代理服务器作为客户端与目标...

    niodemo.zip

    7. **NIO的应用场景**:Java NIO广泛应用于网络编程,如服务器端的高性能Web应用、聊天服务器、游戏服务器等。同时,它也适用于文件系统的大量I/O操作,例如文件复制、文件读写等。 8. **NIO的局限性**:虽然NIO...

    Nio非阻塞socket通信demo

    Java NIO(New IO)是Java 1.4版本引入的一种新的I/O API,它提供了非阻塞式的I/O操作,极大地提高了Java处理I/O的能力。在这个“Nio非阻塞socket通信demo”中,我们可以深入理解NIO在Socket通信中的应用。 1. **...

    JavaIO和NIO练习

    Java IO(Input/Output)和NIO(New IO)是Java平台中用于处理输入和输出操作的核心库。这两个系统提供了不同的方式来读取和写入数据,分别适用于不同类型的场景和需求。 Java IO体系主要基于流(Stream)的概念,...

    nio操作demo

    本示例代码主要展示了如何使用Java NIO(Non-blocking I/O)技术实现一个简单的数据服务器。该服务器通过多个`SelectorLoop`实例处理客户端连接请求,并利用`ServerSocketChannel`监听指定端口上的新连接。 #### 二...

    服务端以NIO的方式处理请求的Demo

    在Java编程领域,NIO(Non-blocking Input/Output,非阻塞I/O)是一种重要的I/O模型,相较于传统的BIO(Blocking I/O),NIO提供了更高效的数据传输方式,尤其适用于高并发、低延迟的场景。本Demo展示了如何在服务端...

    java基础练习demo

    最后,Java Socket通信是实现客户端-服务器应用程序的基础。`Socket`类代表网络连接的一端,`ServerSocket`用于监听客户端的连接请求。通过`Socket`的输入输出流,我们可以发送和接收数据。例如,`Socket....

    Java springboot 整合mina 框架,nio通讯基础教程,mina框架基础教程.zip

    总结来说,本教程将引导你从理论到实践,掌握Java NIO的基本原理,理解Mina框架的使用,以及如何在SpringBoot环境中整合Mina实现高效的网络通信。通过这些知识的学习,你将具备开发高并发、高性能网络应用的能力。

    java发送http/https请求(get/post)Demo,亲测可用

    在Java中,可以使用`HttpURLConnection`类或者第三方库如Apache HttpClient、OkHttp来实现。以下是一个使用`HttpURLConnection`的简单示例: ```java import java.io.BufferedReader; import java.io....

    Java即时通讯系统demo

    7. **数据序列化与反序列化**:Java的`java.io`和`java.nio`包提供了序列化和反序列化的功能,用于将对象转换为可传输的字节流,以便在网络间进行数据交换。 8. **用户界面设计**:虽然源代码没有具体提到UI,但...

    java-socketio推送demo

    这个"java-socketio推送demo"将展示如何在Java后端构建一个服务器,以及如何与网页客户端进行交互。 一、Netty-SocketIO简介 Netty-SocketIO是Java版的Socket.IO实现,它基于高性能的Netty网络库,提供了WebSocket...

Global site tag (gtag.js) - Google Analytics