`

NIO Socket 编程

    博客分类:
  • java
阅读更多
Java NIO (Nonblocking IO)解决了常规IO的瓶颈:

a. 服务端的监听操作会阻塞而无法处理其它事务。多线程方式受到线程池和系统资源的限制,同步操作将会变得复杂。多线程操作磁盘将会导致响应慢甚至死锁。

b. 普通I/O通过Stream来操作,开发简单,但是对I/O的控制力弱

c. 普通IO的读取或写入会在JVM内存和操作系统内存之间进行复制,开销较大。

普通客户端和服务端的Socket通信:

Socket 编程 - 单个客户端


下面通过ServerSocketChannelSocketChannel来实现客户端和服务端的NIO通信:



1. 服务端
public class TCPEchoServerNIO {

	private static final int BUFSIZE = 256;
	private static final long TIMEOUT = 3000;

	public static void main(String[] args) throws IOException {
		args = new String[1];
		args[0] = "4451";

		Selector selector = Selector.open(); // 工厂模式创建选择器

		ServerSocketChannel servChan = ServerSocketChannel.open(); // 工厂模式创建服务套接字通道
		servChan.socket().bind(new InetSocketAddress(Integer.parseInt(args[0]))); // 绑定到指定端口
		servChan.configureBlocking(false); // 非阻塞模式(必须)
		servChan.register(selector, SelectionKey.OP_ACCEPT); // 通道注册到选择器中。是通道的方法,不是选择器的。

		while (true) {
			if (selector.select(TIMEOUT) == 0) { // 获取可进行I/O操作的通道的SelectionKey集
				System.out.println("No channel needs I/O operations");
				continue;
			}

			Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
			while (iter.hasNext()) {
				SelectionKey key = iter.next();
				if (key.isAcceptable()) { // key关联的通道已准备好接收套接字连接(当客户端connect服务端时)
					handleAccept(key);
				}

				if (key.isReadable()) { // key关联的通道已准备好读取(客户端发送的数据已到达)
					handleRead(key);
				}

				if (key.isValid() && key.isWritable()) { // key有效(自key创建后,至调用cancel()、或通道关闭,或selector关闭之前),且通道已准备好写(客户端读取服务端返回)
					handleWrite(key);
				}
				
				iter.remove(); // 每次select()都会append新的SelectionKey,所以移除已处理的,防止下次重复处理
			}
		}
	}

	private static void handleAccept(SelectionKey key) throws IOException, ClosedChannelException {
		SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept();
		if (clntChan != null) {
			clntChan.configureBlocking(false); // 通道必须为非阻塞模式
			clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); // 注册通道的读取事件(当客户端发送数据到服务端时发生),并创建一个缓冲区存放传输数据
		}
	}

	private static void handleRead(SelectionKey key) throws IOException {
		SocketChannel clntChan = (SocketChannel) key.channel();
		ByteBuffer buf = (ByteBuffer) key.attachment(); // 获取key关联的附件,也就是前面创建的缓冲区
		int bytesRead = clntChan.read(buf); // 将客户端发过来的数据放到缓冲区
		if (bytesRead == -1) { // 从客户端读取到的数据为空(当客户端关闭通道时,会触发该读取事件),说明已关闭
			clntChan.close();
		} else if (bytesRead > 0) {
			key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); // 接收 客户端发送数据事件 | 客户端读取服务端返回事件
		}
	}

	private static void handleWrite(SelectionKey key) throws IOException {
		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(); // 缓冲区剩余的数据,移动到缓冲区的起始部位,给下次存放数据腾出空间
	}
}


2. 客户端

public class TCPEchoClientNIO {

	public static void main(String[] args) throws IOException {
		args = new String[3];
		args[0] = "localhost";
		args[1] = "Hello NIO";
		args[2] = "4451";
		
		String server = args[0];
		
		byte[] words = args[1].getBytes(); // 待发送字符串的字节数组形式
		
		int servPort = args.length == 3 ? Integer.parseInt(args[2]) : 4451;
		
		// 静态工厂方法创建通道,设置为非阻塞模式
		SocketChannel clntChan = SocketChannel.open();
		clntChan.configureBlocking(false);
		
		// 初始化连接,并等待连接过程结束
		// 调用非阻塞通道的方法总是立即返回而不管是否完成,所以要采用轮询方式获取状态
		if (!clntChan.connect(new InetSocketAddress(server, servPort))) { // 触发服务端通道的OP_ACCEPT
			while (!clntChan.finishConnect()) {
				System.out.println("Wait for connecting finished");
			}
		}
		
		ByteBuffer writeBuf = ByteBuffer.wrap(words);  // 发送缓冲区
		ByteBuffer readBuf = ByteBuffer.allocate(words.length); // 接收缓冲区,长度和发送数据相同(回显)
		int totalBytesRcvd = 0;
		int bytesRcvd;
		while (totalBytesRcvd < words.length) {
			if (writeBuf.hasRemaining()) { // 发送缓冲区还有数据
				clntChan.write(writeBuf); // 向服务器发送数据,触发通道的OP_READ
			}
			bytesRcvd = clntChan.read(readBuf); // 获取服务端返回的数据,触发通道的OP_WRITE
			totalBytesRcvd += bytesRcvd;
			System.out.println("Receiving " + bytesRcvd + " bytes");
		}

		System.out.println("Received: " + new String(readBuf.array(), 0, totalBytesRcvd)); // 转化为字符串
		
		clntChan.close(); // 关闭通道,触发通道的OP_READ,数据为空
	}
}
  • 大小: 53 KB
分享到:
评论

相关推荐

    NIO socket编程小例子 加法服务器

    总结来说,"NIO socket编程小例子 加法服务器"是一个很好的学习NIO网络编程的起点。通过这个实例,我们可以了解NIO Channel、Buffer和Selector的基本用法,以及如何构建一个简单的网络通信应用。对于任何想要提升...

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

    《NIO与Socket编程技术指南》是一本深入探讨Java NIO(New Input/Output)和Socket编程的专业书籍,由高洪岩撰写。本书主要针对Java开发者,旨在帮助他们理解和掌握这两种在开发网络应用中至关重要的技术。 Java ...

    nio socket编程java代码示例,客户端发送消息,服务端接收

    在Socket编程中,NIO使得服务器可以处理更多的连接请求,而无需为每个连接创建新的线程。 一、服务端实现 服务端的核心是创建一个ServerSocketChannel,监听指定的端口,并注册到Selector。当有新的连接请求时,...

    默蓝网络通信测试工具(NIOSocket工具)支持TCP/IP和HTTP通信-网络通信开发人员必备

    总的来说,"默蓝网络通信测试工具(NIOSocket工具)"凭借其对TCP/IP和HTTP通信的支持,以及Java NIO Socket编程的优势,为网络通信开发人员提供了全面的测试解决方案,是进行网络通信开发和优化的得力助手。...

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

    非常详细地讲解了NIO中的缓冲区、通道、选择器、编码,以及使用Socket技术实现TCP/IP和UDP编程,细化到了演示全部SocketOption的特性,这对理解基于NIO和Socket技术为基础所开发的NIO框架是非常有好处的,本书以案例...

    NioSocket,包括server端和client端

    在Java编程中,NIO(New Input/Output)提供了一种不同于传统IO模型的I/O操作方式,其核心特点是能够进行多路复用,即一个线程可以同时处理多个连接,这使得NioSocket在高并发场景下具有较好的性能表现。 服务器端...

    Java NIO Socket基本

    **Socket编程在NIO中的应用**: 1. **ServerSocketChannel**:用于监听客户端连接,通过调用`ServerSocketChannel.open()`创建,然后绑定到指定的IP和端口,并调用`accept()`方法接收客户端连接。 2. **...

    java NIO socket聊天

    Java NIO(Non-blocking Input/Output)是一种在Java中实现高效网络编程的机制,相比于传统的BIO(Blocking I/O),NIO提供了更好的并发性能。在Java NIO中,服务器端可以使用一个线程来处理多个客户端连接,这被...

    一般Socket客户端与Mina NIO Socket客户端对比示例

    在IT行业中,网络编程是不可或缺的一部分,而Socket通信则是实现网络连接的基础。本文将通过一个对比实例,探讨一般Socket客户端与Mina NIO (Non-blocking I/O) Socket客户端的差异和特点,帮助开发者理解这两种技术...

    读书笔记:学习nio和socket项目参考于《NIO与Socket编程技术与指南》.zip

    读书笔记:学习nio和socket项目参考于《NIO与Socket编程技术与指南》

    网络与nio

    标题中的“网络与nio”指的是Java的非阻塞I/O(Non-blocking Input/Output),它是一种高效的I/O处理...在实践中,需要注意的是,虽然NIO提供了更高的并发性能,但它的编程模型相对复杂,需要更深入的理解和调试技巧。

    Socket编程指南及示例程序.doc

    ### Socket编程指南及示例程序知识点详解 #### 1. 前言 Socket编程作为一项基础且重要的网络编程技术,对于理解现代互联网工作原理及其应用程序设计至关重要。无论是初学者还是有一定经验的开发者,掌握Socket编程...

    基于NIO的socket举例

    基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例基于NIO的socket举例 基于NIO的socket举例

    socket编程(源码)

    本资料提供的是用Socket编程实现聊天程序。 有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,...

    java nio 实现socket

    ### Java NIO 实现Socket通信详解 #### 一、NIO与传统IO的区别及优势 在探讨如何使用Java NIO实现Socket通信之前,我们需要先理解NIO(Non-blocking I/O,非阻塞I/O)与传统阻塞I/O之间的区别。 **传统阻塞I/O...

    读书笔记:NIO与Socket编程技术指南.zip

    读书笔记:NIO与Socket编程技术指南

    读书笔记:NIO与Socket编程技术指南例子.zip

    读书笔记:NIO与Socket编程技术指南例子

    基于TCP的Socket编程服务器和客户端代码

    本主题聚焦于使用Java进行基于TCP的Socket编程,通过`File_client.java`和`File_server.java`两个文件来实现服务器和客户端之间的数据交换。 首先,TCP Socket编程的基础是Java的`java.net.Socket`和`java.net....

Global site tag (gtag.js) - Google Analytics