`
Fly_m
  • 浏览: 259412 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

java nio Selector的使用-服务器端

阅读更多

  前些时候花了一些时间在研究java.nio的api使用机制,看了好久,也觉得不习惯它的使用方式和用法.毕竟自己对C语言了解太少,也不太了解C语言在网络编程上的用法。对这种底层下的编程太不习惯,还是应该好好了解下底层的东西,要不然就光会使用别人的东西,如果是自己写一个,就写不出来了。


  从java1.4以来,java nio就出现在java的api中,在日常的使用当中,基本上都是围绕着java.io中的几个inputStream(outputStream)和reader(writer)在转,要想编写一些其他形式的调用,还真不会。我也看了下最新的springframework中的FileCopyUtils中的代码,也是将各种操作集合给java.io来做。好像java.nio用得不是很多。看了下java.nio的描述信息,感觉这是用在网络编程上的。比如文件下载服务,通信服务等地方。自己暂时还用不上网络上的编程,不过等到用的时候还去学,就太晚了。


  看了下关于Selector的使用方法,官方的说法是一个“多路复用器”,从我自己的角度来讲就感觉像一个服务总线,负责统一各个处理程序的消息注册,统一接收客户端或服务器消息信息,再分发给不同的事件处理程序进行处理。整个流程就一个注册->接收->处理的过程,从使用者的角度来讲,直接使用这些api还不太成熟,毕竟这些api都太底层了,需要了解太多的技术细节,也不太适合像我这种不了解C语言网络编程的人。这周花了三天的时候专门研究了下整个java.nio包,重点看了下关于Selector的运用(datagram和pipe还不太会用),结合了网络上的很多例子(尤其是《java nio》这本书上的例子),对selector总算有了很大的认识,对底层的io编程也有了新的了解。
   

  写了个模拟下载的例子,服务器端模拟一个拥有整个硬盘资源的处理程序。客户端通过发送要下载的文件(通过完整文件路径),从而实现由服务器写文件到客户端,客户端保存接收的整体流程。其中,仅涉及到了数据传输的基本运用,即没有运用到网络编程上的urlConnection,也没有用到专门的socket,客户端也没有实现一个文件多线程下载的机制。仅仅作为一个selector的下载练习使用(当然,如果要求不高,也可以用到实际编程的)。

 

  服务器端基本思路就是打开链接,绑定端口,接收信息,处理信息。详细过程如下:

  第一步:创建服务器端socketChannel,并绑定指定端口,注册到selector上。

selector = Selector.open();
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(1234));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

  都是标准的步骤,先open,再配置block为异步的,服务器socket绑定本机端口,注册到selector上,并指定key为ACCEPT。

  第二步:接收消息,处理信息咯。

for(; ;) {
	selector.select();
	Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
	while(keyIterator.hasNext()) {
	SelectionKey key = keyIterator.next();
	if(key.isValid())
		handle(key);
	keyIterator.remove();
	}
}

 

  这也是标准步骤,先进行select,再获得selectedKey,迭代,处理,再remove掉。

在网上,看到有些例子中,对selector.select()中返回的值进行判断,如果返回为o则continue,在我这个程序中,经测试当selector.select()返回为0时,而selector.selectedKeys()确不为0,这样就没有处理信息了。从官方doc上来看,关于select()的返回值解释为“已更新其准备就绪操作集的键的数目,该数目可能为零”,即这个数目指已更新的键集,故在处理中可能键集没有更新,而选择的消息处理keys却不为0,这种情况是正确的。不清楚是不是这个意思,还望高人来解释一下。

  第三步:就是handle方法了,处理消息事件。

if(key.isAcceptable()) {
	ServerSocketChannel channel = (ServerSocketChannel) key.channel();
	SocketChannel socketChannel = channel.accept();
	socketChannel.configureBlocking(false);
	socketChannel.register(selector, SelectionKey.OP_READ);//注册读事件
	map.put(socketChannel, new Handle());//把socket和handle进行绑定
}
//用map中的handle处理read和write事件,以模拟多个文件同时进行下载
if(key.isReadable() || key.isWritable()) {
	SocketChannel socketChannel = (SocketChannel) key.channel();
	final Handle handle = map.get(socketChannel);
	if(handle != null)
	    handle.handle(key);
}

 

  在以上方法中,我在主方法中仅处理appcet事件,再为每个连接到的socketChannel注册读事件,再在读消息处理中注册写事件。而读和写消息处理,我用了一个内部类来处理,即每个内部类来绑定一个socketChannel,单独处理每个socketChannel。这样的处理,是满足客户端对服务器端发起多个请求,来下载不同的文件,这样服务器端就可为不同的客户端socketChannel定制不同的处理程序了。内部类的定义如下:

private class Handle{
	private StringBuilder message;
	private boolean writeOK = true;
	private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
	private FileChannel fileChannel;
	private String fileName;
}

   message指由客户端发送的信息,在此定义此信息为客户端请求的文件信息。由message来得到服务器端的文件名路径信息,并保存到fileName中,fileChannel即为由此fileName取得的channel。byteBuffer就是用来写数据的字节数据缓冲器了。

  handle单独处理读和写事件,在读事件中,解析文件名,并注册写事件,代码如下:

if(key.isReadable()) {
	SocketChannel socketChannel = (SocketChannel) key.channel();
	if(writeOK)
		message = new StringBuilder();
	while(true) {
		byteBuffer.clear();
		int r = socketChannel.read(byteBuffer);
		if(r == 0)
			break;
		if(r == -1) {
			socketChannel.close();
			key.cancel();
		     return;
		}
		message.append(new String(byteBuffer.array(), 0, r));
	}
//将接收到的信息转化成文件名,以映射到服务器上的指定文件
	if(writeOK && invokeMessage(message)) {
		socketChannel.register(selector, SelectionKey.OP_WRITE);
		writeOK = false;
	}
}

 以上代码就主要是读信息,并解析信息成一个文件名,并注册写事件了。当然还处理客户端断开连接事件,读到信息为-1时,断开连接。其中处理文件信息代码如下:

String m = message.toString();
try {
	File f = new File(m);
	if(!f.exists())
		return false;
	fileName = m;
	return true;
} catch(Exception e) {
	return false;
}

 

其中就是将message转化成一个fileName,以供在写的时候能够从fileName中取得fileChannel,此方法保存fileName是存在的。

  下面看写事件的处理:

//向客户端写数据
if(key.isWritable()) {
	if(!key.isValid())
		return;
	SocketChannel socketChannel = (SocketChannel) key.channel();
	if(fileChannel == null)
		fileChannel = new FileInputStream(fileName).getChannel();
	byteBuffer.clear();
	int w = fileChannel.read(byteBuffer);
//如果文件已写完,则关掉key和socket
	if(w <= 0) {
		fileName = null;
		fileChannel.close();
		fileChannel = null;
		writeOK = true;
		socketChannel.close();
		key.channel();
		return;
	}
     byteBuffer.flip();
     socketChannel.write(byteBuffer);
}

   写处理中,主要就是打开本地的文件channel将fileChannel中的数据写到socketChannel中,如果数据已经写完毕,则关掉相应channel。

 

  至此,服务器端的信息就处理完毕,运行这个程序就只需要在main方法中,new().call()就可以了。当然,有服务器端还需要客户端才行,客户端信息请参照下一笔记。

  服务器端代码随附件中。

分享到:
评论
5 楼 xiaoxi0324 2014-07-15  
永志_爱戴 写道
楼主,你的写文件的代码应该有问题。int w = fileChannel.read(byteBuffer);  这段代码不可能一下子把全部的文件内容读到缓冲区,socketChannel.write(byteBuffer);  也可能存在没有将缓冲区全部数据都写出去的可能。所以应该在循环中读入,然后写出比较好。
再就是你这个文件发送完就把socket给关了,如果客户端还要请求文件只能重新连接,这样多不好啊。为什么不保持这个连接啊。我想看一下,如果写完文件不关闭这个连接你将如何处理


前一段说的问题确实存在,会有buffer不完全的情况。
后面的不关闭socket不予置评
4 楼 永志_爱戴 2014-03-19  
楼主,你的写文件的代码应该有问题。int w = fileChannel.read(byteBuffer);  这段代码不可能一下子把全部的文件内容读到缓冲区,socketChannel.write(byteBuffer);  也可能存在没有将缓冲区全部数据都写出去的可能。所以应该在循环中读入,然后写出比较好。
再就是你这个文件发送完就把socket给关了,如果客户端还要请求文件只能重新连接,这样多不好啊。为什么不保持这个连接啊。我想看一下,如果写完文件不关闭这个连接你将如何处理
3 楼 lxzh504 2013-07-30  
我感觉写的挺好的,当然了,初学者可能看不懂,但是写的逻辑性挺强的,谢谢。
2 楼 magicbloodelf06 2013-01-04  
我是专门上来骂的,写的什么鸟东西,害老子瞎折腾好久,还是不太明白。本想随便找个网页搜点资料看看,就找到这,弄了半天没明白,老子实在火了,把Java NIO这本书下载了下来,找到讲解socket的章节,看了一会,思路原理瞬间清晰明了!!此贴害人不浅,强烈推荐直接去下JAVA NIO这书看,相关内容也没多少,花的时间绝对比看这贴少,学的绝对比这贴多。楼主自己半桶水就别出来害人!!!
1 楼 lonelybug 2009-07-22  
小错误一个最后的一个代码块里面17行应该是
key.cancel(),拼写错了而已。哈哈

文章写得不错。

不过,其实map可以用selectionKey.attach(Object);
来做一个,其实这个的目的也就是跟Event Driven的addListener差不多。

相关推荐

    Java_NIO-Selector.rar_java nio_selector

    总的来说,Java NIO的Selector机制是实现高并发、低延迟I/O服务的关键,尤其适用于需要处理大量并发连接的服务器端场景,如聊天服务器、流媒体服务器等。通过理解并熟练运用Selector,开发者可以构建出更为高效、...

    nio.rar_NIO_NIO-socket_java nio_java 实例_java.nio

    服务端程序”提示我们,这个实例是一个服务器端的应用,它利用Java NIO来处理客户端的连接请求。在NIO服务端编程中,通常会使用`Selector`来监听多个通道的事件,当有新的连接、数据可读或写操作完成时,`Selector`...

    Java NIO实现多个客户端之间的消息互发,客户端与服务器完整代码

    在Java NIO的服务器端,通常会创建一个服务器套接字通道(ServerSocketChannel),它监听客户端的连接请求。每当有新的连接建立时,服务器都会注册这个新的套接字通道到选择器,以便后续监控其读写事件。同时,每个...

    Java-NIO非阻塞服务器示例.docx

    本资源主要讲解了Java-NIO非阻塞服务器的示例,通过使用Java-NIO包来实现非阻塞的服务器端模式。下面是从代码中提取的知识点: 一、Java-NIO包简介 Java-NIO(New I/O)包是Java 1.4版本中引入的新IO处理机制,...

    Java NIO英文高清原版

    Netty是一个基于NIO的高性能、异步事件驱动的网络应用框架,它简化了网络编程,广泛应用于服务器端应用开发。 NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。以下是对这些核心概念的...

    基于matrix-nio和nio-template构建的管理员和个人协助.zip

    Matrix-NIO和NIO-Template是两个用于网络编程的开源库,主要应用于Java环境中,用于提高服务器端的I/O性能和处理能力。Matrix-NIO是一个轻量级的、高性能的网络通信框架,它利用Java NIO(非阻塞I/O)技术来实现高效...

    Java NIO Selector用法详解【含多人聊天室实例】

    服务器端创建一个ServerSocketChannel,监听客户端的连接请求。每当有新的连接建立或已有连接有数据可读时,Selector会通知服务器。服务器根据Selector的结果处理每个连接,读取客户端发送的数据,并将响应广播回...

    java nio 实现socket

    1. **创建ServerSocketChannel**:服务器端首先需要创建一个`ServerSocketChannel`,用于监听客户端的连接请求。 2. **创建Selector**:创建一个`Selector`对象,用于监听`ServerSocketChannel`上的连接事件。 3. **...

    【IT十八掌徐培成】Java基础第27天-02.NIO-ServerSocketChannel-SocketChannel.zip

    ServerSocketChannel是Java NIO中用于服务器端监听客户端连接的通道。通过调用`ServerSocketChannel.open()`方法可以创建一个ServerSocketChannel对象,然后通过`bind()`方法绑定到指定的IP和端口上。当有客户端...

    java nio服务器

    1. **创建ServerSocketChannel**:使用ServerSocketChannel.open()方法打开一个服务器端通道,并调用bind()方法绑定一个端口。 2. **注册选择器**:使用ServerSocketChannel的register()方法将服务器通道注册到选择...

    java NIO实例

    在`NIOServer.java`中,服务器端首先会初始化Selector并注册ServerSocketChannel,之后进入一个无限循环,调用selector.select()方法等待事件发生。当有客户端连接时,服务器会处理新连接,创建SocketChannel,并将...

    java NIO推送实例

    这个实例中的"Desk"和"DeskAppServer"可能分别代表客户端和服务器端的程序,它们通过NIO进行通信,实现了消息的推送。具体的实现细节需要查看源代码,包括通道的创建、缓冲区的使用、选择器的注册和选择等关键步骤。...

    JavaNIO服务器实例Java开发Java经验技巧共6页

    - **服务器端的创建**:使用`ServerSocketChannel`打开并绑定到特定端口,然后将其注册到选择器上,监听ACCEPT事件。 - **客户端连接处理**:当有客户端连接请求时,选择器会通知服务器,服务器从`SocketChannel`...

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

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

    Java NIO Socket基本

    NIO在Java 1.4版本引入,其设计目标是提供一种更高效、更灵活的I/O操作方式,特别适合处理大量并发连接的场景,如服务器端编程。在NIO中,我们不再像BIO那样等待一个操作完成,而是通过选择器(Selector)监控多个...

    java-nio.rar_NIO_java nio

    Java NIO,全称为New Input/Output,是Java在1.4...理解并熟练运用这些知识点,对于构建高性能的Java服务器端应用至关重要。通过阅读"java-nio.pdf"这份文档,你将能够深入学习Java NIO技术及其在异步连接池中的应用。

    一个java NIO的例子

    本例子中的"NioServer"可能是一个简单的Java NIO服务器端程序,用于演示如何使用NIO进行网络通信。下面我们将深入探讨Java NIO的关键组件和工作原理。 1. **通道(Channel)**:通道是数据传输的途径,类似于传统的...

Global site tag (gtag.js) - Google Analytics