3种下载文件程序的思考,为何使用NIO进行异步网络通讯(三)

2014-11-24 01:22:42 · 作者: · 浏览: 7
me);

// 3.1 服务器回复客户端信息(response)
OutputStream os = server.getOutputStream();

byte[] bfile = new byte[1024];

// 往客户端写
while (fileInputStream.read(bfile) > 0) {
os.write(bfile);
}

fileInputStream.close();

os.close();

// 4.关闭socket
// 先关闭输入流
is.close();

// 最后关闭socket
server.close();

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}

System.out.println("服务-----------请求结束over");
}

}
}

以上的Server就是在原始的socket基础上加了线程,每一个Client请求过来后,整个Server主线程不必处于阻塞状态,接收请求后直接另起一个新的线程来处理和客户端的交互,就是往客户端发送二进制包。这个在新线程中虽然阻塞,但是对于服务主线程没有阻塞的影响,主线程依然通过死循环监听着客户端的一举一动。另一个客户端的线程发起请求后就再起一个新的线程对象去为客户端服务。执行效果如下


2个线程互不影响,各自下载各自的。当然从非常严格的意义来讲,str变量在十分高并发的情况下有线程安全问题,这个咱暂且忽略,就着眼于低并发的情况。这个问题是什么呢,就是如果客户端请求比较多了,那么为每一个客户端开辟一个新的线程对象来处理网络传输的请求,需要创建个线程对象,而且这个线程对象从时间上来讲还是处于长连接,这个就比较消费系统资源,这个打开进程管理器就可以看到。而且每一个线程内部都是阻塞的,也没有说完全利用好这个新创建的线程。还拿刚才上厕所举例子,好比现在不止一个坑位了,来了一个用户我这边就按照工程师的厕所坑位图建立一个新的坑位,客户来了,不用等待老坑位,用新创建的坑位就行了。等那个老坑位用完了,自然有垃圾回收器去消灭那个一次性的坑位的,腾出资源位置为了建立新的坑位。长时间连接的意思,相当于这个人上厕所的时间非常长,便秘??需要拉一天才能爽完……老的坑位一时半会儿回收不了,新的坑位需要有空间为其建造茅房以便满足客户端的“急切方便”需要。久而久之,线程数目一多,系统就挂了的概率就增多了(谁也别想上,全玩完了)。
4. 异步非阻塞

使用JDK1.4的NIO可以适当的解决上面的问题,异步 I/O 是一种 没有阻塞地读写数据的方法。通常,在代码进行 read() 调用时,代码会阻塞直至有可供读取的数据。同样, write() 调用将会阻塞直至数据能够写入。异步 I/O 调用不会阻塞。相反,您将注册对特定 I/O 事件的兴趣 可读的数据的到达、新的套接字连接,等等,而在发生这样的事件时,系统将会告诉您。异步 I/O 的一个优势在于,它允许您同时根据大量的输入和输出执行 I/O。同步程序常常要求助于轮询,或者创建许许多多的线程以处理大量的连接。使用异步 I/O,您可以监听任何数量的通道上的事件,不用轮询,也不用额外的线程。还是举上公共厕所例子,虽然这个例子有点臭臭的。您现在有“便便”的需求了,不用那么麻烦,看看公共厕所是否有人占领,也不用给您另起个新坑位,您就拿一根我们服务端定制的容器和一个很粗管子,这个坐便器的大小因您那个地方的尺寸而定,坐便器往您的那个地方一放,再将坐便器和管子一连接,OK,您就敞开了“爽”吧。不用担心,这个管子自然会连接到相应的肥料厂家,将您的排泄物有效回收加以利用的。您完了事,擦擦屁股,关上管子该干嘛还干嘛就行了。另一个人也有这个需求,没问题,每个要我们提供服务的人都用这根管子,和自己的坐便器就行了,管子很粗,谁来连这个管子都行,有多少都行啊。下面我们来看基于NIO的网络下载程序
Java代码
package server;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;

/**
*
* @author liuyan
*
*/
public class NIOServer {
static int BLOCK = 500*1024;

/**
* 处理客户端的内部类,专门负责处理与用户的交互
*/
public class HandleClient {
protected FileChannel channel;
protected ByteBuffer buffer;
String filePath;

/**
* 构造函数,文件的管道初始化
* @param filePath
* @throws IOExceptio