今天突然间想用nio实现个Echo服务,程序实现起来实现不算困难,但跑起来后,在Server端的ServerSocket完成accept之后,我的CPU总是跳到100%。嗯,小郁闷,后来,才发现自己在Server端注册了多余的监听事件SelectionKey.OP_WRITE,改过来后好多了,希望记住这个教训。
EchoServer.java
- package edu.dlut.zxf.nio;
- import java.io.IOException;
- import java.net.InetAddress;
- 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.util.Set;
- /**
- * Echo服务器
- * @author finux
- */
- public class EchoServer {
- public final static int BUFFER_SIZE = 1024; //默认端口
- public final static String HOST = "210.30.107.17";
- public final static int PORT = 8888;
- public static void main(String[] args) {
- ServerSocketChannel ssc = null;
- //缓冲区
- ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
- Selector selector = null;
- try {
- selector = Selector.open();
- ssc = ServerSocketChannel.open();
- ssc.socket().bind(new InetSocketAddress(InetAddress.getByName(HOST), PORT));
- ssc.configureBlocking(false);
- ssc.register(selector, SelectionKey.OP_ACCEPT);
- print("服务器启动,准备好连接...");
- while (selector.select() > 0) {
- Set<SelectionKey> selectionKeys = selector.selectedKeys();
- for (SelectionKey key: selectionKeys) {
- if (key.isAcceptable()) {
- SocketChannel sc = ssc.accept();
- print("有新的连接!地址:" + sc.socket().getRemoteSocketAddress());
- sc.configureBlocking(false);
- sc.register(selector, SelectionKey.OP_READ);
- // 不要写成:
- // sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
- // 毕竟这样多注册的无用的事件SelectionKey.OP_WRTE
- // 如果是这样,在完成accept后,CPU也许会跑到100%
- }
- //same to if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
- if (key.isReadable()) {
- SocketChannel sc = (SocketChannel)key.channel();
- print("有新的读取!地址:" + sc.socket().getRemoteSocketAddress());
- buffer.clear();
- sc.read(buffer);
- buffer.flip();
- byte[] b = new byte[buffer.limit()];
- buffer.get(b);
- String s = new String(b);
- if (s.equals("bye")) {
- print("断开连接:" + sc.socket().getRemoteSocketAddress());
- //断开连接后,取消此键的通道到其选择器的注册
- key.cancel();
- sc.close();
- continue;
- }
- print("读取的内容为:" + s);
- buffer.clear();
- s = "echo: " + s;
- buffer.put(s.getBytes());
- buffer.flip();
- sc.write(buffer);
- }
- }
- selectionKeys.clear();
- }
- } catch(IOException e) {
- e.printStackTrace();
- }
- }
- private static void print(String s) {
- System.out.println(s);
- }
- }
package edu.dlut.zxf.nio; import java.io.IOException; import java.net.InetAddress; 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.util.Set; /** * Echo服务器 * @author finux */ public class EchoServer { public final static int BUFFER_SIZE = 1024; //默认端口 public final static String HOST = "210.30.107.17"; public final static int PORT = 8888; public static void main(String[] args) { ServerSocketChannel ssc = null; //缓冲区 ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); Selector selector = null; try { selector = Selector.open(); ssc = ServerSocketChannel.open(); ssc.socket().bind(new InetSocketAddress(InetAddress.getByName(HOST), PORT)); ssc.configureBlocking(false); ssc.register(selector, SelectionKey.OP_ACCEPT); print("服务器启动,准备好连接..."); while (selector.select() > 0) { Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (SelectionKey key: selectionKeys) { if (key.isAcceptable()) { SocketChannel sc = ssc.accept(); print("有新的连接!地址:" + sc.socket().getRemoteSocketAddress()); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); // 不要写成: // sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); // 毕竟这样多注册的无用的事件SelectionKey.OP_WRTE // 如果是这样,在完成accept后,CPU也许会跑到100% } //same to if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) { if (key.isReadable()) { SocketChannel sc = (SocketChannel)key.channel(); print("有新的读取!地址:" + sc.socket().getRemoteSocketAddress()); buffer.clear(); sc.read(buffer); buffer.flip(); byte[] b = new byte[buffer.limit()]; buffer.get(b); String s = new String(b); if (s.equals("bye")) { print("断开连接:" + sc.socket().getRemoteSocketAddress()); //断开连接后,取消此键的通道到其选择器的注册 key.cancel(); sc.close(); continue; } print("读取的内容为:" + s); buffer.clear(); s = "echo: " + s; buffer.put(s.getBytes()); buffer.flip(); sc.write(buffer); } } selectionKeys.clear(); } } catch(IOException e) { e.printStackTrace(); } } private static void print(String s) { System.out.println(s); } }
EchoClient.java
- package edu.dlut.zxf.nio;
- import java.util.Set;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.net.InetSocketAddress;
- import java.net.InetAddress;
- import java.nio.ByteBuffer;
- import java.nio.channels.SelectionKey;
- import java.nio.channels.Selector;
- import java.nio.channels.SocketChannel;
- /**
- * Echo客户端
- * @author finux
- */
- public class EchoClient {
- public static void main(String[] args) {
- ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE);
- Selector selector = null;
- SocketChannel sc = null;
- try {
- selector = Selector.open();
- sc = SocketChannel.open();
- sc.configureBlocking(false);
- sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT));
- print("客户端启动,准备连接...");
- if (sc.isConnectionPending()) {
- sc.finishConnect();
- }
- print("完成连接");
- sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
- boolean writed = false;
- boolean down = false;
- while (!down && selector.select() > 0) {
- Set<SelectionKey> selectionKeys = selector.selectedKeys();
- for (SelectionKey key: selectionKeys) {
- //int ops = key.readyOps();
- //if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) {
- if (key.isWritable() && !writed) {
- System.out.print("Input(bye to end): ");
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- String s = br.readLine();
- if (s != null && !s.trim().equals("")) {
- buffer.clear();
- buffer.put(s.getBytes());
- buffer.flip();
- sc.write(buffer);
- writed = true;
- if (s.equals("bye")) {
- down = true;
- break;
- }
- }
- }
- //if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) {
- if (key.isReadable() && writed) {
- buffer.clear();
- sc.read(buffer);
- buffer.flip();
- byte[] b = new byte[buffer.limit()];
- buffer.get(b);
- print(new String(b));
- writed = false;
- }
- }
- selectionKeys.clear();
- }
- } catch(IOException e) {
- e.printStackTrace();
- }
- }
- private static void print(String s) {
- System.out.println(s);
- }
- }
package edu.dlut.zxf.nio; import java.util.Set; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; /** * Echo客户端 * @author finux */ public class EchoClient { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE); Selector selector = null; SocketChannel sc = null; try { selector = Selector.open(); sc = SocketChannel.open(); sc.configureBlocking(false); sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT)); print("客户端启动,准备连接..."); if (sc.isConnectionPending()) { sc.finishConnect(); } print("完成连接"); sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); boolean writed = false; boolean down = false; while (!down && selector.select() > 0) { Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (SelectionKey key: selectionKeys) { //int ops = key.readyOps(); //if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) { if (key.isWritable() && !writed) { System.out.print("Input(bye to end): "); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String s = br.readLine(); if (s != null && !s.trim().equals("")) { buffer.clear(); buffer.put(s.getBytes()); buffer.flip(); sc.write(buffer); writed = true; if (s.equals("bye")) { down = true; break; } } } //if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) { if (key.isReadable() && writed) { buffer.clear(); sc.read(buffer); buffer.flip(); byte[] b = new byte[buffer.limit()]; buffer.get(b); print(new String(b)); writed = false; } } selectionKeys.clear(); } } catch(IOException e) { e.printStackTrace(); } } private static void print(String s) { System.out.println(s); } }
当然EchoClient也可以像下面这样来实现:
EchoClient2.java
- package edu.dlut.zxf.nio;
- import java.util.Set;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.net.InetSocketAddress;
- import java.net.InetAddress;
- import java.nio.ByteBuffer;
- import java.nio.channels.SelectionKey;
- import java.nio.channels.Selector;
- import java.nio.channels.SocketChannel;
- /**
- * Echo客户端2
- * @author finux
- */
- public class EchoClient2 {
- public static void main(String[] args) {
- ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE);
- Selector selector = null;
- SocketChannel sc = null;
- try {
- selector = Selector.open();
- sc = SocketChannel.open();
- sc.configureBlocking(false);
- sc.register(selector, SelectionKey.OP_CONNECT);
- sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT));
- print("客户端启动,准备连接...");
- boolean writed = false;
- boolean down = false;
- while (!down && selector.select() > 0) {
- Set<SelectionKey> selectionKeys = selector.selectedKeys();
- for (SelectionKey key: selectionKeys) {
- //int ops = key.readyOps();
- //if ((ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT) {
- if (key.isConnectable()) {
- print("完成连接!");
- if (sc.isConnectionPending()) {
- sc.finishConnect();
- }
- sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
- }
- //if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) {
- if (key.isWritable() && !writed) {
- //从准备IO中读取内容
- System.out.print("Input(bye to end): ");
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- String s = br.readLine();
- if (s != null && !s.trim().equals("")) {
- buffer.clear();
- buffer.put(s.getBytes());
- buffer.flip();
- sc.write(buffer);
- writed = true;
- if (s.equals("bye")) {
- down = true;
- break;
- }
- }
- }
- //if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) {
- if (key.isReadable() && writed) {
- buffer.clear();
- sc.read(buffer);
- buffer.flip();
- byte[] b = new byte[buffer.limit()];
- buffer.get(b);
- print(new String(b));
- writed = false;
- }
- }
- selectionKeys.clear();
- }
- } catch(IOException e) {
- e.printStackTrace();
- }
- }
- private static void print(String s) {
- System.out.println(s);
- }
- }
package edu.dlut.zxf.nio; import java.util.Set; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; /** * Echo客户端2 * @author finux */ public class EchoClient2 { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE); Selector selector = null; SocketChannel sc = null; try { selector = Selector.open(); sc = SocketChannel.open(); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_CONNECT); sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT)); print("客户端启动,准备连接..."); boolean writed = false; boolean down = false; while (!down && selector.select() > 0) { Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (SelectionKey key: selectionKeys) { //int ops = key.readyOps(); //if ((ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT) { if (key.isConnectable()) { print("完成连接!"); if (sc.isConnectionPending()) { sc.finishConnect(); } sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); } //if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) { if (key.isWritable() && !writed) { //从准备IO中读取内容 System.out.print("Input(bye to end): "); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String s = br.readLine(); if (s != null && !s.trim().equals("")) { buffer.clear(); buffer.put(s.getBytes()); buffer.flip(); sc.write(buffer); writed = true; if (s.equals("bye")) { down = true; break; } } } //if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) { if (key.isReadable() && writed) { buffer.clear(); sc.read(buffer); buffer.flip(); byte[] b = new byte[buffer.limit()]; buffer.get(b); print(new String(b)); writed = false; } } selectionKeys.clear(); } } catch(IOException e) { e.printStackTrace(); } } private static void print(String s) { System.out.println(s); } }
但是这样的话,显然EchoClient2中的while循环中的for循环(若有n次),在每次循环中都会多出n-1次if判断,就是下面这个:
- if (key.isConnectable()) {
if (key.isConnectable()) {
所以,我个人更喜欢第一个EchoClient,呵呵,不用注册SelectionKey.OP_CONNECT监听事件。呵呵...
相关推荐
在"echo"代码示例中,Netty展示了如何实现一个简单的回显服务器,该服务器接收到客户端发送的数据后,会原封不动地将数据返回给客户端。这是一个基础但非常重要的概念,它帮助我们理解网络通信的基本原理,并为构建...
本篇将基于给定的标题“采用NIO实现一个Socket服务器”来详细阐述如何使用NIO进行Socket服务端的开发。 首先,理解NIO的核心概念: 1. **通道(Channel)**:通道是连接到I/O设备的途径,可以读取或写入数据。常见的...
本篇文章将深入探讨如何使用Java NIO(非阻塞I/O)来实现阻塞多线程通信,这对于高性能服务器端应用尤其重要。我们将会分析`EchoServer.java`、`EchoClient.java`和`SocketUtils.java`这三个文件中的关键知识点。 ...
在本教程中,我们将详细讨论如何使用Java编程语言实现一个基于UDP(User Datagram Protocol)的Echo服务。 UDP是传输层的一个无连接协议,与TCP相比,它不提供诸如确认、流量控制或重传等机制。这意味着UDP通信更轻...
相比之下,NIO服务器的实现方式会有所不同,它使用选择器来管理多个连接,并且只用一个线程来处理所有连接。不过,由于这个示例的压缩包中并未包含NIO服务器的代码,因此无法提供具体的NIO代码示例。但你可以查阅...
通过以上步骤,我们能够利用`ServerBootstrap` 和Java NIO实现一个高效的服务器,它能同时处理大量并发连接,是构建大规模、高并发网络应用的理想选择。在实际开发中,根据项目需求,我们还需要考虑异常处理、连接...
本示例通过两个类——EchoServer和EchoClient,展示了如何使用NIO实现基于TCP/IP协议的网络通信。 首先,我们来看`EchoServer`。这个类通常扮演服务端的角色,监听指定端口并接收客户端连接。在Java NIO中,服务器...
在给定的部分代码示例中,可以看到一个简单的Java IO Echo Server实现。这个例子展示了如何使用传统的Java IO来实现一个服务器端的应用程序,它可以接收客户端发送的消息,并将接收到的信息原样返回给客户端。这里...
11-Ping-Channel使用Java NIO(非阻塞I/O)的DatagramChannel实现,而10-Ping-Datagram则使用了传统的Socket DatagramPacket。 6. **Eclipse IDE**:00-EclipseIDE可能是提供Eclipse集成开发环境的一些配置或插件...
- **性能优化**:例如使用NIO(非阻塞I/O)提高服务器处理能力,或者实现连接池管理客户端连接。 - **日志记录**:用于追踪和诊断服务器的运行情况。 通过分析这些文件,我们可以学习到如何从零开始构建一个简单的...
- **文件传输服务**:使用 Netty 实现大文件的断点续传功能。 通过学习和实践这些示例代码,你可以深入了解 Netty 的工作原理,以及如何利用它来构建高效、稳定的网络应用。在实际项目中,Netty 可用于构建高性能...
6. **NIO实现**: `Listener`类是一个内部类,继承自`Thread`,用于监听套接字并创建处理任务。这种设计允许服务器高效地处理多个并发连接,避免了传统阻塞I/O可能导致的性能瓶颈。 理解Hadoop的RPC机制有助于...
这个实战教程和代码示例是为那些希望深入理解并运用 Netty 的开发者准备的。以下是对 Netty 的详细介绍以及如何通过代码示例进行学习。 一、Netty 框架基础 Netty 是由 JBoss 组织开源的一个网络通信框架,基于 ...
- **示例程序**:通过示例代码展示如何使用Netty实现SPDY服务器和客户端。 ### 第十一部分:通过UDP广播事件 - **UDP基础**:介绍UDP协议的基本概念和特点。 - **UDP广播**:探讨如何使用Netty进行UDP广播。 - **...
- 使用Java NIO的`ServerSocketChannel`和`SocketChannel`来处理服务器和客户端的连接,`Selector`和`SelectionKey`用于实现非阻塞特性,确保高效率的并发处理。 - `Buffer`类作为数据的临时存储,`FileChannel`则...
通过阅读和实践这些示例代码,开发者可以掌握如何在Java中实现基本的网络服务和客户端应用。而标签中的"工具"可能指的是这些示例代码可以作为开发网络应用时的参考或基础工具。 总的来说,Java Socket的单线程阻塞...
- 用户可以通过实现ChannelInboundHandler或ChannelOutboundHandler接口来处理这些事件。 4. **Netty的编码和解码** - Netty提供了多种编码器和解码器,如LengthFieldBasedFrameDecoder用于处理带有长度字段的...
Netty4EchoDemo是一个基于Netty 4框架实现的简单回显客户端(echo client)和服务器(echo server)示例。这个项目适用于那些想要学习如何使用Netty进行网络编程的开发者,尤其是对Java NIO(非阻塞I/O)感兴趣的...
真正的NIO实现会使用`ByteBuffer`和`SocketChannel`的read/write方法进行数据交换。 **总结**: Java NIO为开发者提供了更灵活的I/O操作方式,特别是对于需要处理大量并发连接的网络应用,如聊天服务器、大型游戏...
4. 使用Java NIO的ServerSocketChannel和SocketChannel处理服务器与客户端的连接,并通过Selector实现非阻塞I/O,提高效率。 5. 利用Selector监听通道的可读写事件,SelectionKey管理不同通道的状态。 6. 数据的临时...