Java之NIO传输数据
NIO可谓陈词旧调,不值一提. 但之前都是泛泛而谈, 现在深入应用才知道秘诀所在. 对于SocketChannel有read()与write(),但由于"非阻塞IO"本质, 这二个方法的返回值提示其字符数目. 说白点, 就是你得有个措施解决可能一次不能完成的操作. 否则, 你在服务端的数据会莫名其妙地乱码, 莫名其妙地不见...
还有另一个关键之处就是Buffer的应用, 重用Buffer的时候务必注意, position, limit的标点. 下面是实质源码:
private void onAccept(SelectionKey key) {
logger.debug("处理Accept事件");
SocketChannel sc = null;
try {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
sc = ssc.accept();
/* 判断其是否可以连接 */
String client = ((InetSocketAddress) sc.socket().getRemoteSocketAddress()).getAddress().getHostAddress();
if (config.senders.containsKey(client)) {
/* 前缀格式<系统>+<服务器IP>+ */
String prefix = config.senders.getProperty(client);
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ, new AttachObject(encoder.encode(prefix.toString(), config.charset)));
logger.info(String.format("发送者%s连接成功, 记录前缀:%s", client, prefix));
} else {
logger.info(String.format("发送者%s连接拒绝", client));
sc.close();
}
} catch (Exception e) {
logger.error("处理Accept事件错误!", e);
if (sc != null) {
try {
sc.close();
} catch (IOException e1) {
logger.error("关闭异常Socket错误!", e1);
}
}
}
}
使用InetAddress.getHostAddress()才能获取实际意义上的IP.
private void onRead(SelectionKey key) {
/* 必须注意NIO可能无法一次接收完全部数据 */
logger.debug("处理Read事件");
int ret = 0;
int size = 0;
SocketChannel sc = null;
try {
sc = (SocketChannel) key.channel();
AttachObject attach = (AttachObject) key.attachment();
if (attach.idx < 4) {
ret = sc.read(attach.sizeBuf);
if (ret == -1) {
logger.debug("客户端输入流已关闭!");
sc.close();
sc = null;
return;
} else {
attach.idx += ret;
}
}
if (attach.idx == 4) {
attach.sizeBuf.flip();
size = attach.sizeBuf.getInt();
attach.tot = 4 + size;
if (attach.dataBuf.capacity() < size) {
attach.dataBuf = ByteBuffer.allocate(size);
}else {
attach.dataBuf.limit(size);/* 必须限制可读字节数,否则可能读多 */
}
}
if (attach.idx >= 4 && attach.idx < attach.tot) {
ret = sc.read(attach.dataBuf);
if (ret == -1) {
logger.debug("客户端输入流已关闭!");
sc.close();
sc = null;
return;
} else {
attach.idx += ret;
}
}
if (attach.idx == attach.tot) {
attach.dataBuf.flip();
cache.put((byte[]) attach.attach, attach.dataBuf.array(), 0, attach.dataBuf.limit());
attach.reset();
}
} catch (Exception e) {
logger.error("处理Read事件错误!", e);
if (sc != null) {
try {
sc.close();
} catch (IOException e1) {
logger.error("关闭异常Socket错误!", e1);
}
}
}
}
每个Key要有独享的Attachment来保存中间信息, 使用Buffer读取或写入字节务必注意其返回值. 必须在字节数完全读完才能去解码.
public void run() {
ByteBuffer sizeBuf = ByteBuffer.allocate(4);
int idx = 0;
int tot = 0;
LinkedList<byte[]> batch = new LinkedList<byte[]>();
try {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress(outer.config.receiverHost, outer.config.receiverPort));
outer.scList.add(sc);
while (!Thread.currentThread().isInterrupted()) {
batch.clear();
if (outer.cache.get(batch, outer.config.senderBatchSize, true) > 0) {
for (byte[] data : batch) {
/* 必须注意,NIO有可能不会一次写完Buffer的字节 */
idx = 0;
tot = 4 + data.length;
sizeBuf.clear();
sizeBuf.putInt(data.length);
sizeBuf.flip();
do {
idx += sc.write(sizeBuf);
} while (idx < 4);
ByteBuffer dataBuf = ByteBuffer.wrap(data);
do {
idx += sc.write(dataBuf);
} while (idx < tot);
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
使用Buffer写字节数据也必须注意其返回值, 在未达到预期时, 使用循环继续.
以上三个方法是Socket NIO的关键所在. 当你接收到的数据乱码的时候,你会想起这些...
Java之NIO传输数据的更多相关文章
- JAVA bio nio aio
[转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...
- java的nio之:java的nio系列教程之buffer的概念
一:java的nio的buffer==>Java NIO中的Buffer用于和NIO通道Channel进行交互.==>数据是从通道channel读入缓冲区buffer,从缓冲区buffer ...
- java的nio之:java的nio系列教程之channel的概念
一:java的nio的channel Java NIO的通道类似流,但又有些不同: ==>既可以从通道中读取数据,又可以写数据到通道.但流的读写通常是单向的. ==>通道可以异步地读写. ...
- java的nio之:java的nio系列教程之概述
一:java的nio的核心组件?Java NIO 由以下几个核心部分组成: ==>Channels ==>Buffers ==>Selectors 虽然Java NIO 中除此之外还 ...
- java之NIO编程
所谓行文如编程,随笔好比java文件,文章好比类,参考文献是import,那么目录就是方法定义. 本篇文章处在分析thrift的nonblocking server之前,因为后者要依赖该篇文章的知识. ...
- 输入和输出--java的NIO
Java的NIO 实际开发中NIO使用到的并不多,我并不是说NIO使用情景不多,是说我自己接触的并不是很多,前面我在博客园和CSDN上转载了2篇别人写的文章,这里来大致总结下Java的NIO,大概了解 ...
- JAVA 探究NIO
事情的开始 1.4版本开始,java提供了另一套IO系统,称为NIO,(New I/O的意思),NIO支持面向缓冲区的.基于通道的IO操作. 1.7版本的时候,java对NIO系统进行了极大的扩展,增 ...
- 理解Java的NIO
同步与阻塞 同步和异步是针对应用程序和内核的交互而言的. 同步:执行一个操作之后,进程触发IO操作并等待(阻塞)或者轮询的去查看IO的操作(非阻塞)是否完成,等待结果,然后才继续执行后续的操作. 异步 ...
- Java通过NIO实现快速文件拷贝的代码
将内容过程重要的内容片段做个记录,下面的内容段是关于Java通过NIO实现快速文件拷贝的内容. public static void fileCopy( File in, File out ) thr ...
随机推荐
- oracle的sqlldr并行导入表不要加索引
ORA-26002: Table string has index defined upon it. Cause: Parallel load was specified into a table w ...
- NOIP2007 守望者的逃离-DP
https://vijos.org/p/1431 描述 恶魔猎手尤迪安野心勃勃,他背叛了暗夜精灵,率领深藏在海底的娜迦族企图叛变.守望者在与尤迪安的交锋中遭遇了围杀,被困在一个荒芜的大岛上.为了杀死守 ...
- NULL值比较,两个列的合并,列值按条件替换。
show create table 表名 -- 显示创建表的sql语句. 为已有的表增加新列.alter table 表名 add 列名 int NULL -- 此行加了一个int 类型 默认可以nu ...
- SVN 外部引用(svn:externals)处理相似系统的公用代码
一.创建外部引用 我们常常遇到这样一个场景,我们有两个系统,两个系统用的是同一套框架.如果我们用两套程序 去做,当我们修改这个公共的框架的时候,另外一个还是旧版本的,很容易造成混乱. SVN的外部用就 ...
- tech
流式计算框架storm.spark.genfire.esper(CEP)
- wpf依赖属性、绑定实现原理、附加属性学习
依赖属性和普通属性相比节省内存的原因:对于普通属性,每个对象有需要存储一个普通属性的值,即便是默认值.而依赖属性的默认值是静态的存储在类中的,所有对象都使用同一默认值,所以对于拥有大量属性的控件来说这 ...
- 学生信息管理系统应用ios源码iPad版
学生信息管理系统应用iPad版,该应用源码比较完整的,而且也很详细,这也是一款学校用的学生和老师管理系统,里面涉及到了很多ipad常用的控件,操作和数据存储. <ignore_js_op> ...
- linux下怎么编译运行C语言程序?
linux下的C语言编译器是gcc,C++的编译器是g++. linux下编程可以使用编辑器vi或vim,建议使用vim,因为它有语法高亮显示.程序编写好后,假设你的程序名为test.c,可以使用gc ...
- Linux下vi编辑器粘贴复制剪切功能
RedHat 9.0 Linux下vi编辑器实现简单的粘贴复制剪切功能": 如果想把文件内的第三行内容黏贴到第十五行: 1.进入你的文件,(处于命令模式,而不是编辑模式) 2.将你的光标移到 ...
- scala学习笔记2
一.算术和操作符重载 a + b 是如下方法的简写: a.+(b) 在scala中你可以使用任何符号来为方法命名.比如BigInt类就定义了一个/%的方法,该方法返回一个对偶,对偶的内容是除法操作得到 ...