`
finux
  • 浏览: 202511 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

用nio实现Echo服务

    博客分类:
  • Java
阅读更多

今天突然间想用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监听事件。呵呵...

4
0
分享到:
评论
1 楼 albrich 2011-12-31  
确实很好,不过你的注释好像错了

相关推荐

    采用NIO实现一个Socket服务器

    本篇将基于给定的标题“采用NIO实现一个Socket服务器”来详细阐述如何使用NIO进行Socket服务端的开发。 首先,理解NIO的核心概念: 1. **通道(Channel)**:通道是连接到I/O设备的途径,可以读取或写入数据。常见的...

    使用UDP实现Echo服务.rar_Echo Echo_java udp

    在本教程中,我们将详细讨论如何使用Java编程语言实现一个基于UDP(User Datagram Protocol)的Echo服务。 UDP是传输层的一个无连接协议,与TCP相比,它不提供诸如确认、流量控制或重传等机制。这意味着UDP通信更轻...

    Netty 演示 (Netty案例大全).zip

    2.10.1JUnit 5.5.2示例包含示例如下Java标准I/O实现Echo服务器、客户端Java NIO实现Echo服务器、客户端Java AIO实现Echo服务器、客户端Netty实现Echo服务器、客户端Netty 实现 丢弃服务器Netty 实现时间服务器Java ...

    NIO实现网络通信,直接就能跑

    本示例通过两个类——EchoServer和EchoClient,展示了如何使用NIO实现基于TCP/IP协议的网络通信。 首先,我们来看`EchoServer`。这个类通常扮演服务端的角色,监听指定端口并接收客户端连接。在Java NIO中,服务器...

    Java Socket学习---nio实现阻塞多线程通信

    本篇文章将深入探讨如何使用Java NIO(非阻塞I/O)来实现阻塞多线程通信,这对于高性能服务器端应用尤其重要。我们将会分析`EchoServer.java`、`EchoClient.java`和`SocketUtils.java`这三个文件中的关键知识点。 ...

    ServerBootstrap实现nio.rar_NIO_ServerBootstrap_heart8w9_java nio

    通过以上步骤,我们能够利用`ServerBootstrap` 和Java NIO实现一个高效的服务器,它能同时处理大量并发连接,是构建大规模、高并发网络应用的理想选择。在实际开发中,根据项目需求,我们还需要考虑异常处理、连接...

    java-nio.rar_java nio_nio 对象实例化

    总结来说,这个压缩包的内容可能涵盖了如何在Java NIO中实例化和使用通道、缓冲区、选择器,以及如何实现一个简单的多线程服务器,利用选择器来监听和处理来自多个客户端的非阻塞I/O请求。这些内容对于理解和使用...

    NIO与传统IO代码区别实例

    相比之下,NIO服务器的实现方式会有所不同,它使用选择器来管理多个连接,并且只用一个线程来处理所有连接。不过,由于这个示例的压缩包中并未包含NIO服务器的代码,因此无法提供具体的NIO代码示例。但你可以查阅...

    java io 与java nio区别

    在给定的部分代码示例中,可以看到一个简单的Java IO Echo Server实现。这个例子展示了如何使用传统的Java IO来实现一个服务器端的应用程序,它可以接收客户端发送的消息,并将接收到的信息原样返回给客户端。这里...

    《Netty实战》中的 echo 代码-netty-echo.zip

    在"echo"代码示例中,Netty展示了如何实现一个简单的回显服务器,该服务器接收到客户端发送的数据后,会原封不动地将数据返回给客户端。这是一个基础但非常重要的概念,它帮助我们理解网络通信的基本原理,并为构建...

    小型Web服务器实现

    - **性能优化**:例如使用NIO(非阻塞I/O)提高服务器处理能力,或者实现连接池管理客户端连接。 - **日志记录**:用于追踪和诊断服务器的运行情况。 通过分析这些文件,我们可以学习到如何从零开始构建一个简单的...

    Java Socket编程实例(四)- NIO TCP实践

    通过以上分析,我们可以看到Java NIO在TCP编程中的强大之处,以及如何通过`EchoProtocol`接口和`TCPEchoSelectorProtocol`类实现一个简单的回显服务器。在实际应用中,这种模型可以扩展到更复杂的网络服务,例如聊天...

    以netty4.1源码中的EchoServer为例对netty的源码进行分析.docx

    在 EchoServer 示例中,接收到的数据会被原样返回,这是 Netty 中典型的回显服务,用于测试网络通信的正确性。`bossGroup` 处理新的连接,一旦连接建立,`workerGroup` 就接手处理 I/O 事件,包括接收和发送数据。...

    Java NIO深入分析

    真正的NIO实现会使用`ByteBuffer`和`SocketChannel`的read/write方法进行数据交换。 **总结**: Java NIO为开发者提供了更灵活的I/O操作方式,特别是对于需要处理大量并发连接的网络应用,如聊天服务器、大型游戏...

    Netty4EchoDemo

    Netty4EchoDemo是一个基于Netty 4框架实现的简单回显客户端(echo client)和服务器(echo server)示例。这个项目适用于那些想要学习如何使用Netty进行网络编程的开发者,尤其是对Java NIO(非阻塞I/O)感兴趣的...

    NIO面试。。。。。。。

    - 视频连线通常使用 RTP(Real-time Transport Protocol)和 RTCP(Real-time Transport Control Protocol)组合实现。 10. **浏览器输入 URL 到页面显示的过程**: - 用户输入 URL -&gt; DNS 解析 -&gt; 发起 ...

    Netty 框架学习 —— 第一个 Netty 应用(csdn)————程序.pdf

    在本篇关于“Netty框架学习——第一个Netty应用”的文章中,我们将深入理解如何使用Netty构建一个简单的Echo服务器和客户端。Netty是一个高性能、异步事件驱动的网络应用程序框架,广泛应用于Java领域的服务器开发。...

    Java Socket编程实例(五)- NIO UDP实践

    本文将深入探讨如何使用NIO与UDP结合,实现一个简单的回显服务器,即UDPEchoSelectorProtocol。 首先,我们来看一下`EchoProtocol`接口。这个接口定义了处理Socket通道中接受、读取和写入操作的方法。`handleAccept...

    Netty:基于Java NIO的网络服务器Netty生产实例

    Netty作为之前及现在不断学习Netty道路上持续集成项目Netty心跳实现客户端及服务端聊天实现完成Netty回声服务器使用WebSocket实现点对点聊天功能WebSocket实现群聊功能及上下线提醒增加Netty UDP协议实现使用第三方...

    java文件传输.pdf

    4. 使用Java NIO的ServerSocketChannel和SocketChannel处理服务器与客户端的连接,并通过Selector实现非阻塞I/O,提高效率。 5. 利用Selector监听通道的可读写事件,SelectionKey管理不同通道的状态。 6. 数据的临时...

Global site tag (gtag.js) - Google Analytics