`
yixiandave
  • 浏览: 140568 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

NIO学习笔记1

阅读更多
这是第一篇NIO学习笔记,至于会不会有第二篇到时候再说

最近也是刚刚开始接触NIO,主要用于替换ServerSocket

备忘:cat</dev/tcp/ip/port可以直接创建tcp连接,不过只能显示服务器的返回信息,把小于号改成大于号就可以向服务器发送消息

双向的话还是用telnet比较好。当然这是没来得及写客户端情况下的应付手段

首先是ServerSocketChannel的例子
首先是开启服务器
	//创建一个选择器
	selector = Selector.open();
	//创建ServerSocketChannel并将其绑定在端口上
	ServerSocketChannel server = ServerSocketChannel.open();
	server.bind(new InetSocketAddress(PORT));
	//设置为非阻塞模式
	server.configureBlocking(false);
	//注册这个channel
	server.register(selector, SelectionKey.OP_ACCEPT);

这里我刚开始的时候犯了个错误,bind的时候new InetSocketAddress我输入了localhost和port两个参数,结果造成服务器只能通过localhost访问
我从路由器设置到selinux到iptables来回检查了无数便,最后netstat -na发现了真相。顿时有种吐血的感觉。

之后就进入一个while true循环获取事件了
			while(true){
				// select操作会把发生关注事件的Key加入到selectionKeys中(只管加不管减)
				if(selector.select()==0){
					continue;
				}
				//获取发生了关注时间的KEY集合
				//selector.keys()会返回所有的SelectionKey(包括没有发生事件的)
				Set<SelectionKey> keys = selector.selectedKeys();
				for(SelectionKey key:keys){
					process(key);
					//移除处理完成的key
					keys.remove(key);
				}
			}


这里我设置了一个Hashtable保存每个连接和key的对应关系,便于应对服务器需要主动向某个地址发送数据的情况
	private void process(SelectionKey key) throws IOException{
		//OP_ACCEPT事件,这个只有ServerSocketChannel会触发
		if(key.isAcceptable()){
			SocketChannel channel = ((ServerSocketChannel)key.channel()).accept();
			//非阻塞
			channel.configureBlocking(false);
			//取得刚注册的key保存到连接map中
			SelectionKey nkey = channel.register(key.selector(),SelectionKey.OP_READ,ByteBuffer.allocate(1024));
			// get address
			InetSocketAddress addr = (InetSocketAddress)channel.getRemoteAddress();
			targetMap.put(addr.toString(), nkey);
		}
		//OP_READ事件,可读取的数据
		if(key.isReadable()){
			TCPReader.processRead(key);
		}
		//OP_WRITE事件,写数据
		if(key.isWritable()){
			TCPWriter.processWrite(key);
		}
	}

【主动通信】
这个虽然是后做的,不过既然提到了还是做一个说明吧
说实话这方面我没有在网上查找到任何可用的资料,完全是自己摸索出来的
首先,连接到服务器的TCP连接的channel可以通过getRemoteAddress()方法取得来源地址,强制转换未InetSocketAddress后可以直接调用.toString()方式,得到的结果是:/ip:port 形式的字符串,通过这个可以很容易区分开每个SelectionKey。刚好可以作为Hashtable的Key

然后就是这个SelectionKey的获取问题了,最开始以为产生OP_ACCEPT事件的key直接就能拿走,测试后发现这是服务器端的Key,如果需要取得对应客户端连接的Key则需要多一个步骤。

我注意到
channel.register(key.selector(),SelectionKey.OP_READ,ByteBuffer.allocate(1024));

这个注册方法返回值就是一个SelectionKey,取出测试后发现就是和客户端连接的Key。
初步目标达成!

不过单单取得SelectionKey没用,还需要完成发送流程,这里我在网上看到有一种设想是把要发送的对象attach到key上然后在Write流程中通过attachment取出,实测后发现此方式数据极容易在Read流程中被当作ByteBuffer读取,出现强制转换类型错误。即使使用ByteBuffer作为介质也可能被Read流程当作输入数据取出。传递方面还是用其他方式吧。

常规的流程先检测Read状态后检测Write状态,但是实测发现直接修改key.interestOps为OP_WRITE状态并没有被检测器捕获,还是要自己另外实现channel的write流程。不过要注意一个channel的write一定要放在同一个线程中,不然极可能将要发送的数据插入到正在发送的数据包中造成数据混乱无法读取。这个大家都是老手就不赘述了。

注意取得channel并调用write方法前一定要把key设置为OP_WRITE模式,否则会报异常

【数据包分离】
TCP数据流一个很大的特点是实际上并不清楚数据包的长度大小,也不清楚何时结束。所有的数据都按顺序叠在一起。因此发送的数据包可能因为过大而被分割成几个小部分发过来,也可能前后两个包首尾相连一起发过来,将数据流按协议一个个剥离出来是一个必须妥善解决的问题。
在此有必要列出我使用的字节流协议详情:
字节序号   内容
0          数据包的类型(视频流、图片、RSA的public key申请、视频流终止信号等)
1-4        4个字节组成int值,表示数据的目标用户id(target)
5-9        4个字节组成的int值,表示正文数据的长度(length)
10-(length+9) 正文数据



读取线程采用此算法(UML学的不好,能看懂最好。。。哈哈),使用了2个ByteArrayOutputStream双缓冲来缓存数据。留有一个填加数据的方法。使用一个static Hashtable管理线程池,在static方法中传入一个SelectionKey,通过key的地址寻找目标线程并将ByteBuffer中的byte[]数据输入stream1缓冲流

而线程中则将stream1中的数据转入stream2中,随后对数据进行读取和处理。处理完一个包后如果还有剩余数据则放回stream2继续处理。线程末尾检查stream1是否有新的数据,如果有则循环,没有就结束线程并从线程池中删除对象。

今天暂时到这里,由于是自学,个人见解难免错漏,也在此抛砖引玉,也希望有高人能给出更有效的处理方法
  • 大小: 57.8 KB
分享到:
评论

相关推荐

    java学习笔记1(java io/nio)

    java学习笔记1(java io/nio)设计模式

    nio demo for nio学习笔记(体系结构以及模块介绍)

    **NIO(Non-blocking Input/Output)**是Java在1.4版本引入的一种新的I/O模型,它提供了与...然而,NIO的学习曲线相对较陡峭,需要对操作系统级别的I/O模型有一定了解,但一旦掌握,将极大地提升系统的性能和可扩展性。

    javaNIO学习笔记

    ### Java NIO 学习笔记 #### 一、概述 Java NIO (Non-Blocking IO,也称为 Java New IO),是 Java 对传统 IO 模型的一次重大改进,旨在提高程序处理大量并发连接的能力。NIO 的核心组件包括 Channels、Buffers 和 ...

    Java NIO学习笔记——ByteBuffer用法

    ByteBuffer的用法是Java NIO学习中的核心内容。 首先,我们了解下ByteBuffer的基本概念。ByteBuffer是一个字节缓冲区,可以存储字节序列。在NIO中,所有的数据读写都通过缓冲区进行,ByteBuffer与其他类型的Buffer...

    JAVA NIO学习笔记.docx

    Java NIO(New Input/Output)是Java标准库在JDK 1.4引入的一组新的I/O API,它提供了一种不同于传统IO的高效、非阻塞的I/O操作方式。NIO的核心概念包括Channel、Buffer和Selector,它们共同构建了一个与操作系统...

    NIO学习笔记

    《NIO学习笔记》 在Java编程领域,NIO(Non-blocking Input/Output,非阻塞I/O)是一种重要的I/O模型,与传统的BIO(Blocking I/O)相对应。NIO提供了一种新的方式来处理I/O操作,特别是在处理大量并发连接时,它的...

    javaNIO学习笔记(csdn)————程序.pdf

    Java NIO,全称Non-Blocking Input/Output,是非阻塞式输入输出,它是Java从1.4版本开始引入的一种新的I/O模型,为Java程序员提供了处理I/O操作的新方式。NIO的主要特点是其能够使Java程序以更有效的方式处理I/O流,...

    java NIO学习系列 笔记

    1. **通道(Channel)**: - 通道是数据传输的双向路径,可以将数据从源读取到目标,也可以将数据从目标写入到源。通道的概念类似于传统的流,但与流不同的是,通道可以同时进行读写操作,并且可以与多个缓冲区交互...

    非常详细javaSE学习笔记.rar

    这份“非常详细JavaSE学习笔记.rar”压缩包显然是一份全面的Java SE学习资源,包含了从基础知识到高级特性的全方位讲解。下面,我们将详细探讨这份笔记可能涵盖的关键知识点。 1. **Java起源与环境搭建**:笔记可能...

    Java JDK 7学习笔记 PDF

    这个PDF学习笔记是开发者深入理解JDK 7特性和功能的重要参考资料。以下是对Java JDK 7的一些核心知识点的详细阐述: 1. **泛型改进**:在JDK 7中,泛型的使用更加灵活,引入了类型推断(Type Inference)特性,通过...

    JAVA学习笔记 林信良

    《JAVA学习笔记》是林信良先生的一部深入浅出的Java编程教程,旨在帮助初学者和有一定经验的开发者巩固和提升Java编程技能。这本书涵盖了Java语言的基础到高级概念,是一份宝贵的自学资料。 首先,从基础部分开始,...

    良葛格JAVA 学习笔记

    《良葛格JAVA 学习笔记》是由知名IT专家林信良,网名“良葛格”,在台湾大学电机工程学系的深厚学术背景基础上,结合其作为SUN教育训练中心讲师的丰富教学经验编写的。他的著作还包括《Spring 技术手册》,并且他...

    javaNIO.xmind

    自己总结的java中NIO的笔记,绘制了详细的思维导图,每个思维导图中均有详细的博文解释,方便大家学习和理解,免费分享给大家。适合java的爱好者和学习者

    Java JDK 6学习笔记——ppt简体版

    Java JDK 6学习笔记是为Java初学者量身定制的一份宝贵资料,它涵盖了Java编程的基础概念、语法以及核心特性。这份PPT简体版旨在帮助读者快速掌握Java开发的基本技能,逐步成为一名合格的Java程序员。 Java JDK...

    Java+JDK6学习笔记

    本篇将围绕“Java+JDK6学习笔记”展开,探讨在JDK6环境下Java编程的核心知识点。 1. **JDK6概述**:JDK6是Oracle公司于2006年发布的Java平台标准版(Java SE)的一个重要版本,它的全称是Java SE 6,带来了许多新...

    Java JDK 6学习笔记.zip

    这个压缩包“Java JDK 6学习笔记.zip”显然是一个包含有关Java JDK 6深入学习资源的集合,可能是PDF文档、笔记或者其他形式的教学材料。 在Java JDK 6中,有几个关键的特性值得我们关注: 1. **改进的性能**:JDK ...

    良葛格Java学习笔记(完整版)

    《良葛格Java学习笔记(完整版)》是一份全面且深入的Java编程教程,适合不同层次的Java学习者,无论你是初学者还是有经验的开发者,都能从中受益。这份笔记详细介绍了Java语言的核心概念、语法特性以及实际开发中的...

    java学习笔记源代码

    Java学习笔记源代码是针对清华大学出版的《Java学习笔记》一书,配套的 JDK1.5 版本的源码。这些源代码旨在帮助读者深入理解Java编程语言,通过实践来加强理论学习,提高编程技能。Java作为一种广泛使用的高级编程...

Global site tag (gtag.js) - Google Analytics