`

在JAVA中使用NIO进行网络编程(转)

阅读更多

 

在JAVA中使用NIO进行网络编程

在JDK中,有一个非常有意思的库:NIO(New I/O)。这个库中有3个重要的类,分别是java.nio.channels中Selector和Channel,以及java.nio中的Buffer。

本篇文章我们首先了解一下为什么需要NIO来进行网络编程,然后看看一步一步来讲解如何在网络编程中使用NIO。

为什么需要NIO

使用Java编写过Socket程序的同学一定都知道Socket和SocketServer。当调用某个调用的时候,调用的地方就会阻塞,等待响应。这种方式对于小规模的程序非常方便,但是对于大型的程序就有点力不从心了,当有大量的连接的时候,我们可以为每一个连接建立一个线程来操作。但是这种做法带来的缺陷也是显而易见的:

  1.  
  2. 硬件能够支持大量的并发。

  3. 并发的数量始终有一个上限。

  4. 各个线程之间的优先级不好控制。

  5. 各个Client之间的交互与同步困难。

我们也可以使用一个线程来处理所有的请求,使用不阻塞的IO,轮询查询所有的Client。这种做法同样也有缺陷:无法迅速响应Client端,同时会消耗大量轮询查询的时间。

所以,我们需要一种poll的模式来处理这种情况,从大量的网络连接中找出来真正需要服务的Client。这正是NIO诞生的原因:提供一种Poll的模式,在所有的Client中找到需要服务的Client。

回到我们刚刚说到的3个最最重要的Class:java.nio.channels中Selector和Channel,以及java.nio中的Buffer。

Channel代表一个可以被用于Poll操作的对象(可以是文件流也可以使网络流),Channel能够被注册到一个Selector中。通过调用Selector的select方法可以从所有的Channel中找到需要服务的实例(Accept,read ..)。Buffer对象提供读写数据的缓存。相对于我们熟悉的Stream对象,Buffer提供更好的性能以及更好的编程透明性(人为控制缓存的大小以及具体的操作)。

配合BUFFER使用CHANNEL

与传统模式的编程不用,Channel不使用Stream,而是Buffer。我们来实现一个简单的非阻塞Echo Client:

package com.cnblogs.gpcuster;

import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class TCPEchoClientNonblocking {
	public static void main(String args[]) throws Exception {
		if ((args.length < 2) || (args.length > 3))// Testforcorrect#ofargs
			throw new IllegalArgumentException(
					"Parameter(s): <Server> <Word> [<Port>]");
		String server = args[0];// ServernameorIPaddress
		// ConvertinputStringtobytesusingthedefaultcharset
		byte[] argument = args[1].getBytes();
		int servPort = (args.length == 3) ? Integer.parseInt(args[2]) : 7;
		// Createchannelandsettononblocking
		SocketChannel clntChan = SocketChannel.open();
		clntChan.configureBlocking(false);
		// Initiateconnectiontoserverandrepeatedlypolluntilcomplete
		if (!clntChan.connect(new InetSocketAddress(server, servPort))) {
			while (!clntChan.finishConnect()) {
				System.out.print(".");// Dosomethingelse
			}
		}
		ByteBuffer writeBuf = ByteBuffer.wrap(argument);
		ByteBuffer readBuf = ByteBuffer.allocate(argument.length);
		int totalBytesRcvd = 0;// Totalbytesreceivedsofar
		int bytesRcvd;// Bytesreceivedinlastread
		while (totalBytesRcvd < argument.length) {
			if (writeBuf.hasRemaining()) {
				clntChan.write(writeBuf);
			}
			if ((bytesRcvd = clntChan.read(readBuf)) == -1) {
				throw new SocketException("Connection closed prematurely");
			}
			totalBytesRcvd += bytesRcvd;
			System.out.print(".");// Dosomethingelse
		}
		System.out.println("Received:" + // converttoStringperdefaultcharset
				new String(readBuf.array(), 0, totalBytesRcvd));
		clntChan.close();
	}
}

这段代码使用ByteBuffer来保存读写的数据。通过clntChan.configureBlocking(false); 设置后,其中的connect,read,write操作都不回阻塞,而是立刻放回结果。

使用SELECTOR

 

 

Selector的可以从所有的被注册到自己Channel中找到需要服务的实例。

我们来实现Echo Server。

首先,定义一个接口:

package com.cnblogs.gpcuster;

import java.nio.channels.SelectionKey;
import java.io.IOException;

public interface TCPProtocol {
	void handleAccept(SelectionKey key) throws IOException;

	void handleRead(SelectionKey key) throws IOException;

	void handleWrite(SelectionKey key) throws IOException;
}
我们的Echo Server将使用这个接口。然后我们实现Echo Server:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

public class TCPServerSelector {
	private static final int BUFSIZE = 256;// Buffersize(bytes)
	private static final int TIMEOUT = 3000;// Waittimeout(milliseconds)

	public static void main(String[] args) throws IOException {
		if (args.length < 1) {// Testforcorrect#ofargs
			throw new IllegalArgumentException("Parameter(s):<Port>...");
		}
		// Createaselectortomultiplexlisteningsocketsandconnections
		Selector selector = Selector.open();
		// Createlisteningsocketchannelforeachportandregisterselector
		for (String arg : args) {
			ServerSocketChannel listnChannel = ServerSocketChannel.open();
			listnChannel.socket().bind(
					new InetSocketAddress(Integer.parseInt(arg)));
			listnChannel.configureBlocking(false);// mustbenonblockingtoregister
			// Registerselectorwithchannel.Thereturnedkeyisignored
			listnChannel.register(selector, SelectionKey.OP_ACCEPT);
		}
		// Createahandlerthatwillimplementtheprotocol
		TCPProtocol protocol = new EchoSelectorProtocol(BUFSIZE);
		while (true) {// Runforever,processingavailableI/Ooperations
		// Waitforsomechanneltobeready(ortimeout)
			if (selector.select(TIMEOUT) == 0) {// returns#ofreadychans
				System.out.print(".");
				continue;
			}
			// GetiteratoronsetofkeyswithI/Otoprocess
			Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
			while (keyIter.hasNext()) {
				SelectionKey key = keyIter.next();// Keyisbitmask
				// Serversocketchannelhaspendingconnectionrequests?
				if (key.isAcceptable()) {
					protocol.handleAccept(key);
				}
				// Clientsocketchannelhaspendingdata?
				if (key.isReadable()) {
					protocol.handleRead(key);
				}
				// Clientsocketchannelisavailableforwritingand
				// keyisvalid(i.e.,channelnotclosed)?
				if (key.isValid() && key.isWritable()) {
					protocol.handleWrite(key);
				}
				keyIter.remove();// removefromsetofselectedkeys
			}
		}
	}
}
 

我们通过listnChannel.register(selector, SelectionKey.OP_ACCEPT); 注册了一个我们感兴趣的事件,然后调用selector.select(TIMEOUT)等待订阅的时间发生,然后再采取相应的处理措施。

最后我们实现EchoSelectorProtocol

package com.cnblogs.gpcuster;

import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.ByteBuffer;
import java.io.IOException;

public class EchoSelectorProtocol implements TCPProtocol {
	private int bufSize;// SizeofI/Obuffer

	public EchoSelectorProtocol(int bufSize) {
		this.bufSize = bufSize;
	}

	public void handleAccept(SelectionKey key) throws IOException {
		SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept();
		clntChan.configureBlocking(false);// Mustbenonblockingtoregister
		// Registertheselectorwithnewchannelforreadandattachbytebuffer
		clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer
				.allocate(bufSize));
	}

	public void handleRead(SelectionKey key) throws IOException {
		// Clientsocketchannelhaspendingdata
		SocketChannel clntChan = (SocketChannel) key.channel();
		ByteBuffer buf = (ByteBuffer) key.attachment();
		long bytesRead = clntChan.read(buf);
		if (bytesRead == -1) {// Didtheotherendclose?
			clntChan.close();
		} else if (bytesRead > 0) {
			// Indicateviakeythatreading/writingarebothofinterestnow.
			key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
		}
	}

	public void handleWrite(SelectionKey key) throws IOException {
		/*
		 * Channelisavailableforwriting,andkeyisvalid(i.e.,clientchannel
		 * notclosed).
		 */
		// Retrievedatareadearlier
		ByteBuffer buf = (ByteBuffer) key.attachment();
		buf.flip();// Preparebufferforwriting
		SocketChannel clntChan = (SocketChannel) key.channel();
		clntChan.write(buf);
		if (!buf.hasRemaining()) {// Buffercompletelywritten?
		// Nothingleft,sonolongerinterestedinwrites
			key.interestOps(SelectionKey.OP_READ);
		}
		buf.compact();// Makeroomformoredatatobereadin
	}
}

在这里,我们又进一步对Selector注册了相关的事件:key.interestOps(SelectionKey.OP_READ);

这样,我们就实现了基于NIO的Echo 系统。

 

分享到:
评论

相关推荐

    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...

    java nio 网络编程指南

    ### Java NIO网络编程核心知识点解析 #### 非阻塞式Socket通信:Java NIO的革命性突破 从JDK 1.4版本开始,Java引入了NIO(Non-blocking I/O)API,这标志着Java网络编程的一个重大转折点。传统上,基于阻塞I/O的...

    一站式学习Java网络编程 全面理解BIO:NIO:AIO1

    本课程旨在帮助学生全面理解 Java 网络编程中的 BIO、NIO、AIO 三剑客,掌握 RPC 编程的基础知识,并结合实战项目巩固所学。 一、网络编程三剑客 - BIO、NIO、AIO BIO(Blocking I/O)是一种同步阻塞式 I/O 模式,...

    《NIO与Socket编程技术指南》_高洪岩

    通过阅读《NIO与Socket编程技术指南》,读者不仅可以理解NIO和Socket的基本原理,还能学习到如何在实际项目中有效地利用这些技术,提升网络应用的性能和可扩展性。这本书对于希望深入学习Java网络编程的开发者来说,...

    java网络编程NIO视频教程

    - **学习目标**:学会如何在实际应用中使用文件锁。 #### 30. Java NIO-Path路径操作 - **主要内容**:介绍Path API的使用方法,包括路径创建、解析等操作。 - **学习目标**:掌握Path API的基本操作。 #### 31. ...

    java网络编程 nio-netty

    java网络编程 nio-netty,想要学习netty的同学,这本书是非常好的资源。

    实现java网络与nio例子

    Java NIO(New Input/Output)是Java标准库提供的一种I/O模型,它与传统的 ...这个"网络与nio"的压缩包文件很可能包含了服务器端和客户端的完整代码示例,可以帮助我们深入理解并实践Java NIO在网络编程中的应用。

    Java网络编程/Java网络编程实例

    9. **异常处理**:网络编程中常见的异常有IOException、SocketException等,良好的异常处理机制可以确保程序的健壮性。 10. **网络调试工具**:如telnet、curl和Wireshark等,可以帮助开发者测试和调试网络应用。 ...

    Java网络编程第三版.pdf

    3. **多线程与并发**:在网络编程中,多线程和并发处理是必不可少的,书中会讲解如何在Java中管理线程,以及如何处理并发问题,如同步和锁机制。 4. **URL和HTTP**:Java通过URL类提供了访问Web资源的能力,而HTTP...

    基于Java NIO的网络编程框架.zip

    本项目深入探讨了Java网络编程中的多种模式,包括BIO(阻塞IO)、NIO(非阻塞IO)、IO多路复用(select、poll、epoll)、Reactor模式,以及零拷贝技术。通过这些实现,项目展示了如何在高并发环境下优化网络通信效率...

    Java网络编程 NIO Netty

    Java网络编程领域中,NIO(Non-blocking Input/Output,非阻塞I/O)和Netty框架是两个关键概念。NIO是一种I/O模型,它与传统的BIO(Blocking I/O)模型不同,BIO在处理连接时一旦进行读写操作就会阻塞,直到数据传输...

    java网络编程第四版pdf

    作者深入浅出地解释了网络分层结构和各层的主要功能,使读者能够明白数据在网络中的传输过程。 第二章“流”是Java网络编程的核心概念。Java中的I/O流提供了处理输入和输出的强大工具,无论是处理文件、网络连接...

    Java.NIO资源下载资源下载

    - **SocketChannel**:介绍了 SocketChannel 的使用,这是在网络编程中常用的一种 Channel。 - **管道 (Pipes)**:讨论了 Pipe Channel 的应用场景及其优势。 - **Channels 工具类**:介绍了 Java NIO 提供的一些...

    《Java程序设计之网络编程》

    3. **输入/输出流**:在网络编程中,数据的传输依赖于Java的输入流和输出流。InputStream和OutputStream用于处理字节流,而Reader和Writer则用于处理字符流。这些流对象通常与Socket关联,用于读写网络上的数据。 4...

    Java网络编程(第4版)PDF

    在Java网络编程中,首要涉及的是I/O模型。书中会介绍基础的套接字(Socket)编程,包括TCP和UDP协议的应用。TCP提供面向连接的服务,确保数据的可靠传输,而UDP则是无连接的,更注重传输效率。读者将学习如何创建和...

    Java NIO英文高清原版

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java平台中用于替代标准I/O(BIO)模型的一种新机制。...学习和理解Java NIO以及Netty的使用,对于提升Java网络编程的能力至关重要。

    java网络编程

    - **并发处理**:在网络编程中,多线程常用于处理并发连接,提高服务器性能。 - **线程安全**:理解如何在多线程环境中正确地同步和共享数据,防止竞态条件。 7. **异常处理**:网络编程中,必须妥善处理...

Global site tag (gtag.js) - Google Analytics