今天突然间想用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);
}
}
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);
}
}
当然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);
}
}
但是这样的话,显然EchoClient2中的while循环中的for循环(若有n次),在每次循环中都会多出n-1次if判断,就是下面这个:
if (key.isConnectable()) {
所以,我个人更喜欢第一个EchoClient,呵呵,不用注册SelectionKey.OP_CONNECT监听事件。呵呵...
分享到:
相关推荐
本篇将基于给定的标题“采用NIO实现一个Socket服务器”来详细阐述如何使用NIO进行Socket服务端的开发。 首先,理解NIO的核心概念: 1. **通道(Channel)**:通道是连接到I/O设备的途径,可以读取或写入数据。常见的...
在本教程中,我们将详细讨论如何使用Java编程语言实现一个基于UDP(User Datagram Protocol)的Echo服务。 UDP是传输层的一个无连接协议,与TCP相比,它不提供诸如确认、流量控制或重传等机制。这意味着UDP通信更轻...
本示例通过两个类——EchoServer和EchoClient,展示了如何使用NIO实现基于TCP/IP协议的网络通信。 首先,我们来看`EchoServer`。这个类通常扮演服务端的角色,监听指定端口并接收客户端连接。在Java NIO中,服务器...
本篇文章将深入探讨如何使用Java NIO(非阻塞I/O)来实现阻塞多线程通信,这对于高性能服务器端应用尤其重要。我们将会分析`EchoServer.java`、`EchoClient.java`和`SocketUtils.java`这三个文件中的关键知识点。 ...
通过以上步骤,我们能够利用`ServerBootstrap` 和Java NIO实现一个高效的服务器,它能同时处理大量并发连接,是构建大规模、高并发网络应用的理想选择。在实际开发中,根据项目需求,我们还需要考虑异常处理、连接...
总结来说,这个压缩包的内容可能涵盖了如何在Java NIO中实例化和使用通道、缓冲区、选择器,以及如何实现一个简单的多线程服务器,利用选择器来监听和处理来自多个客户端的非阻塞I/O请求。这些内容对于理解和使用...
相比之下,NIO服务器的实现方式会有所不同,它使用选择器来管理多个连接,并且只用一个线程来处理所有连接。不过,由于这个示例的压缩包中并未包含NIO服务器的代码,因此无法提供具体的NIO代码示例。但你可以查阅...
在给定的部分代码示例中,可以看到一个简单的Java IO Echo Server实现。这个例子展示了如何使用传统的Java IO来实现一个服务器端的应用程序,它可以接收客户端发送的消息,并将接收到的信息原样返回给客户端。这里...
在"echo"代码示例中,Netty展示了如何实现一个简单的回显服务器,该服务器接收到客户端发送的数据后,会原封不动地将数据返回给客户端。这是一个基础但非常重要的概念,它帮助我们理解网络通信的基本原理,并为构建...
- **性能优化**:例如使用NIO(非阻塞I/O)提高服务器处理能力,或者实现连接池管理客户端连接。 - **日志记录**:用于追踪和诊断服务器的运行情况。 通过分析这些文件,我们可以学习到如何从零开始构建一个简单的...
通过以上分析,我们可以看到Java NIO在TCP编程中的强大之处,以及如何通过`EchoProtocol`接口和`TCPEchoSelectorProtocol`类实现一个简单的回显服务器。在实际应用中,这种模型可以扩展到更复杂的网络服务,例如聊天...
在 EchoServer 示例中,接收到的数据会被原样返回,这是 Netty 中典型的回显服务,用于测试网络通信的正确性。`bossGroup` 处理新的连接,一旦连接建立,`workerGroup` 就接手处理 I/O 事件,包括接收和发送数据。...
真正的NIO实现会使用`ByteBuffer`和`SocketChannel`的read/write方法进行数据交换。 **总结**: Java NIO为开发者提供了更灵活的I/O操作方式,特别是对于需要处理大量并发连接的网络应用,如聊天服务器、大型游戏...
Netty4EchoDemo是一个基于Netty 4框架实现的简单回显客户端(echo client)和服务器(echo server)示例。这个项目适用于那些想要学习如何使用Netty进行网络编程的开发者,尤其是对Java NIO(非阻塞I/O)感兴趣的...
- 视频连线通常使用 RTP(Real-time Transport Protocol)和 RTCP(Real-time Transport Control Protocol)组合实现。 10. **浏览器输入 URL 到页面显示的过程**: - 用户输入 URL -> DNS 解析 -> 发起 ...
在本篇关于“Netty框架学习——第一个Netty应用”的文章中,我们将深入理解如何使用Netty构建一个简单的Echo服务器和客户端。Netty是一个高性能、异步事件驱动的网络应用程序框架,广泛应用于Java领域的服务器开发。...
本文将深入探讨如何使用NIO与UDP结合,实现一个简单的回显服务器,即UDPEchoSelectorProtocol。 首先,我们来看一下`EchoProtocol`接口。这个接口定义了处理Socket通道中接受、读取和写入操作的方法。`handleAccept...
Netty作为之前及现在不断学习Netty道路上持续集成项目Netty心跳实现客户端及服务端聊天实现完成Netty回声服务器使用WebSocket实现点对点聊天功能WebSocket实现群聊功能及上下线提醒增加Netty UDP协议实现使用第三方...
4. 使用Java NIO的ServerSocketChannel和SocketChannel处理服务器与客户端的连接,并通过Selector实现非阻塞I/O,提高效率。 5. 利用Selector监听通道的可读写事件,SelectionKey管理不同通道的状态。 6. 数据的临时...
总的来说,这个实验提供了Java NIO实现文件传输的实践经验,让学生深入理解网络通信的原理,同时熟悉非阻塞I/O模型,这对提升软件开发者的技能至关重要。通过这样的练习,开发者能够更好地设计和实现高并发、高性能...