java nio 客户端_Java Se : Java NIO(服务端)与BIO(客户端)通信

Java目前有三种IO相关的API了,下面简单的说一下:

BIO,阻塞IO,最常用的Java IO API,提供一般的流的读写功能。相信学习Java的人,都用过。

NIO,非阻塞IO,在JDK1.4中开始出现,大量应用与服务器端编程,用于提高并发访问的性能,常用的NIO框架有Netty,Mina。

AIO,异步IO,在JDK1.7开始出现。还没有了解过,等以后了解了再说。

阻塞、非阻塞,同步、异步

在写这篇文章前,在网上了解了一下,其中争议最的问题要数阻塞、非阻塞怎么理解,异步、同步怎么理解。

由于每个人想法的不同,很难达到一个一致的答案,又没有真正的大牛出来给这一个准确的定义。这里也简单的说一下,我对这两组名词的理解。

1)阻塞、非阻塞

我认为,BIO,NIO没有大家想的那么复杂,就是底层实现中进行数据的读写(IO)采取的两种方案,只不过非阻塞读写要比阻塞IO读写更快一些。

bio中的InputStream#read()是一个block方法。

dadbed4930a18382d4465b0909964027.png

2)同步、异步

同步与异步,我认为说的并不是IO本身,我认为说的是程序采用的编程模型,也就是说采用的是同步的编程模型还是异步的编程模型。

BIO、NIO,他们的区别是操作系统读写数据采用的方式,他们是Java中的概念,在Java领域,他们的底层实现采用的是同步的编程模型。所以说BIO、NIO都是同步的。

AIO的底层实现应当是异步的编程模型,所以说它是异步IO。

这里我只是阐述了我对它们的理解,没有与大家争论到底怎么去理解他们。也许我没有大家想的那么深远,毕竟我只是学习了NIO不到一天时间而已。

针对BIO、NIO,服务器编程如何提高性能

一个程序运行的快慢,一般有会受到两个因素的影响:1)程序代码是否高效,2)IO读写是否高效。曾经看过这么一幅图,大致内容是:一帮不同角色的人(程序员、运维、项目经理等角色的人)在一起讨论一个应用程序效率地下的问题。

程序员说的是:给我3个月时间,我能够让程序运行效率提高,当然了,我要调整代码的整体结构…

运维说:…

项目经理说:换用读写更快的硬件设备解决这个问题。

故事我已经无法还原,但是这个故事说的内容就是程序优化带来的效率的提升远不及提高IO速度带来的提升。

相比于BIO,NIO就是从读写来提升效率的。性能对于服务器来说尤为重要,服务器端编程并不是都采用了NIO编程。

Tomcat服务器内部,就有BIO、NIO两种方式。

1)BIO如何提高并发访问

BIO,是一种阻塞IO,服务器端使用BIO进行数据读写时,一般都是采用了一个Socket请求对应一个Thread的方式来提高性能的。

但是一台服务器上,可以跑的线程数量也是有限制的:线程不是越多越好,毕竟线程间的切换,也是有不小的开销。也不是越少越好,线程太少,极端情况下一个线程,如果用一个线程来解决用户的并发访问,服务器接收一个客户的请求时,其他人都要处于等待状态。你访问网页,多数情况下超过5秒,估计你就关掉它了吧。

或者采用线程池方案。

2)采用NIO编程时 如何提高并发访问

采用选择器轮询可用通道,读写数据。具体的怎么做的就不说了,网上一大坨一大坨的,虽然网上大家写的大多是copy别人的。下面给会出一个例子,所以这里就不多说了,不知道的可以网上找相关的文章。

一个Thread下开一个Selector,一个Selector处理多个Socket通道(也就是多个用于请求),这样就是一个Thread线程可以同时处理多个用户请求。

孰优孰劣

假若说,服务器设置同时处理1000个用户请求(也就是1000个处理用户请求的线程)。假若有10000个人来发请求。

如果采用BIO API编程,那么就同时只能为1000个人服务,其他的9000人就处于等待状态。

如果采用NIO API编程,也开启1000个线程,因为一个Thread可以同时处理多个用户请求,咱不说让它处理太多了,就处理10个吧,这样算下来,这个10000个用户请求,就都可以处理了。

BIO(客户端)与NIO(服务端)通信

今天学习了NIO,就用NIO来处理浏览器用户请求吧。浏览器发送的肯定不是采用NIO API发送Socket请求的,肯定是使用了阻塞式IO,也就是对应于Java中的BIO了。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.fjn.other.nio.socket;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.InetSocketAddress;importjava.net.ServerSocket;importjava.net.Socket;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.util.ArrayList;importjava.util.Collection;importjava.util.Iterator;importjava.util.concurrent.Callable;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;

@SuppressWarnings({"unchecked"})public classNioServer {

ServerSocketChannel serverChannel;

ServerSocket serverSocket;public final intport;privateSelector selector;

ByteBuffer buffer= ByteBuffer.allocate(1024);

NioServer(final intport) {this.port =port;

}void init() throwsException {//创建 ServerSocketChannel、ServerSocket

serverChannel=ServerSocketChannel.open();

serverSocket=serverChannel.socket();

serverSocket.bind(newInetSocketAddress(port));//设置通道为非阻塞模式

serverChannel.configureBlocking(false);//开启通道选择器,并注册 ServerSocketChannel

selector=Selector.open();

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

}void go() throwsException {while (true) {int num =selector.select();if (num <= 0)continue;

Iterator keyIter =selector.selectedKeys().iterator();while(keyIter.hasNext()) {final SelectionKey key =keyIter.next();//接收一个Socket连接//key.isAcceptable()如果为true,说明channnel支持accept(),也就是说明是一个ServerSocketChannel

if(key.isAcceptable()) {

SocketChannel clientChannel=serverChannel.accept();if (clientChannel != null) {

clientChannel.configureBlocking(false);

clientChannel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);

}

}//如果isReadable()为true,说明是一个SocketChannel

if(key.isReadable()) {

String requestContent=read(key);//业务处理//responseContent=doSomthing(requestContent);

write(key,"ok" /*responseContent*/);

}

keyIter.remove();

}

}

}//从通道读取数据

String read(SelectionKey key)throwsException {

SocketChannel socketChannel=(SocketChannel) key.channel();

buffer.clear();//这一步必须有

int len = 0;

StringBuffer str=newStringBuffer();while ((len = socketChannel.read(buffer)) > 0) {byte[] bs =buffer.array();

String block=new String(bs, 0, len);

System.out.println("Server read: " +block);

str.append(block);

}

buffer.clear();returnstr.toString();

}//写数据到通道

void write(SelectionKey key, String str) throwsException {

SocketChannel socketChannel=(SocketChannel) key.channel();

buffer.clear();

buffer.put(str.getBytes());

buffer.flip();//这一步必须有

socketChannel.write(buffer);

}public static void main(String[] args) throwsException {final int port = 10000;

NioServer server= newNioServer(port);

server.init();///========================================================//接下来模拟3个Client并发访问服务器

int poolsize = 3;

ExecutorService pool=Executors.newFixedThreadPool(poolsize);

Collection tasks = new ArrayList(10);final String clientname="clientThread";for (int i = 0; i < poolsize; i++) {final int n =i;//若每一个Client都保持使用BIO方式发送数据到Server,并读取数据。

tasks.add(newCallable() {

@Overridepublic Object call() throwsException {

Socket socket= new Socket("127.0.0.1", port);final InputStream input =socket.getInputStream();final OutputStream out =socket.getOutputStream();final String clientname_n = clientname + "_" +n;//BIO读取数据线程

new Thread(clientname_n + "_read") {

@Overridepublic voidrun() {byte[] bs = new byte[1024];while (true) {try{

Thread.sleep(1000);

}catch(InterruptedException e) {

e.printStackTrace();

}int len = 0;try{while ((len = input.read(bs)) != -1) {

System.out.println("Clinet thread "

+Thread.currentThread()

.getName()+ " read: "

+ new String(bs, 0, len));

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

}.start();//BIO写数据线程

new Thread(clientname_n + "_write") {

@Overridepublic voidrun() {int a = 0;while (true) {try{

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

String str=Thread.currentThread().getName()+ " hello, " +a;try{

out.write(str.getBytes());

a++;

}catch(IOException e) {

e.printStackTrace();

}

}

}

}.start();return null;

}

});

}

pool.invokeAll((Collection extends Callable>) tasks);

server.go();

}

}

View Code

上面的测试的是3个Client采用BIO API不断的并发的发送Socket 请求到Server端。Server采用NIO API处理Client的请求并作出响应,然后Client接收响应。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值