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方法。
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了。
package com.fjn.other.nio.socket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @SuppressWarnings({ "unchecked" }) public class NioServer { ServerSocketChannel serverChannel; ServerSocket serverSocket; public final int port; private Selector selector; ByteBuffer buffer = ByteBuffer.allocate(1024); NioServer(final int port) { this.port = port; } void init() throws Exception { // 创建 ServerSocketChannel、ServerSocket serverChannel = ServerSocketChannel.open(); serverSocket = serverChannel.socket(); serverSocket.bind(new InetSocketAddress(port)); // 设置通道为非阻塞模式 serverChannel.configureBlocking(false); // 开启通道选择器,并注册 ServerSocketChannel selector = Selector.open(); serverChannel.register(selector, SelectionKey.OP_ACCEPT); } void go() throws Exception { while (true) { int num = selector.select(); if (num <= 0) continue; Iterator<SelectionKey> 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) throws Exception { SocketChannel socketChannel = (SocketChannel) key.channel(); buffer.clear();// 这一步必须有 int len = 0; StringBuffer str=new StringBuffer(); 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(); return str.toString(); } // 写数据到通道 void write(SelectionKey key, String str) throws Exception { SocketChannel socketChannel = (SocketChannel) key.channel(); buffer.clear(); buffer.put(str.getBytes()); buffer.flip();// 这一步必须有 socketChannel.write(buffer); } public static void main(String[] args) throws Exception { final int port = 10000; NioServer server = new NioServer(port); server.init(); ///======================================================== // 接下来模拟3个Client并发访问服务器 int poolsize = 3; ExecutorService pool = Executors.newFixedThreadPool(poolsize); Collection<Callable> tasks = new ArrayList<Callable>(10); final String clientname="clientThread"; for (int i = 0; i < poolsize; i++) { final int n = i; // 若每一个Client都保持使用BIO方式发送数据到Server,并读取数据。 tasks.add(new Callable() { @Override public Object call() throws Exception { 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") { @Override public void run() { 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") { @Override public void run() { 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<Object>>) tasks); server.go(); } }
上面的测试的是3个Client采用BIO API不断的并发的发送Socket 请求到Server端。Server采用NIO API处理Client的请求并作出响应,然后Client接收响应。
Java Se : Java NIO(服务端)与BIO(客户端)通信的更多相关文章
- JAVA RPC (十) nio服务端解析
源码地址:https://gitee.com/a1234567891/koalas-rpc 企业生产级百亿日PV高可用可拓展的RPC框架.理论上并发数量接近服务器带宽,客户端采用thrift协议,服务 ...
- Java网络编程(TCP服务端)
/* * TCP服务端: * 1.创建服务端socket服务,并监听一个端口 * 2.服务端为了给客户端提供服务,获取客户端的内容,可以通过accept方法获取连接过来的客户端对象 * 3.可以通过获 ...
- “快的打车”创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - V2EX
"快的打车"创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - ...
- java开源即时通讯软件服务端openfire源码构建
java开源即时通讯软件服务端openfire源码构建 本文使用最新的openfire主干代码为例,讲解了如何搭建一个openfire开源开发环境,正在实现自己写java聊天软件: 编译环境搭建 调试 ...
- java http post/get 服务端和客户端实现json传输
注:本文来源于<java http post/get 服务端和客户端实现json传输> 最近需要写http post接口所以学习下. 总的还是不难直接上源码! PostHttpClient ...
- Java的oauth2.0 服务端与客户端的实现
oauth原理简述 oauth本身不是技术,而是一项资源授权协议,重点是协议!Apache基金会提供了针对Java的oauth封装.我们做Java web项目想要实现oauth协议进行资源授权访问,直 ...
- java网络编程TCP传输—流操作—服务端反馈与客户端接收
在读取完流后,服务端会向客户端返回一些数据,告诉客户端,已经写完了. 在这里和”流操作—拿到源后的写入动作“差不多,客户端同样以byte与Buffered两种缓冲读取作为例子,同时,.也是希望大家给补 ...
- NIO 服务端TCP连接管理的方案
最近做的一个项目需要在服务端对连接端进行管理,故将方案记录于此. 方案实现的结果与背景 因为服务端与客户端实现的是长连接,所以需要对客户端的连接情况进行监控,防止无效连接占用资源. 完成类似于心跳的接 ...
- 客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)
后面考虑通过netty做一个真正意义的简约版RPC框架,今天先尝试通过正常调用逻辑调用netty构建的nio服务端并同步获得返回信息.为后面做铺垫 服务端实现 我们先完成服务端的逻辑,逻辑很简单,把客 ...
- NIO服务端主要创建过程
NIO服务端主要创建过程: 步骤一:打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的副管道,示例代码如下: ServerSocketChannel ...
随机推荐
- 电脑Windows使用中遇到的那些问题
Win64位注册表导入方法 64位Windows操作系统注册表不同于32位Windows操作系统,Win64 位版本分为 32 位注册表项和 64 位注册表项.默认 64 位版本注册表编辑器 (Reg ...
- HTTP & HTTPs
HTTP HTTP 消息 HTTP 方法 参考 [1]. HTTP 协议初识 - 阮一峰: HTTPS 参考 [1]. HTTPS 升级指南 - 阮一峰:
- 提高生产性工具 - Model代码生成器(NET / JAVA) (一)
原来在上一家公司,整整一年都在做工具,提高生产性,那个项目特别巨大,所以总共为老东家节约了500K左右的美金. (除了表扬之外,我个人什么好处都没有,领导们都升官发财了,郁闷) 到了新公司,也准备开发 ...
- Topshelf + ServiceModelEx + Nlog 从头构建WCF
前言 Topshelf可以很方便的构建windows service,而且在本地开发时也可以构建Console宿主,因此很方便WCF的开发. ServiceModelEx则提供了很多便利的方法来配置w ...
- MVC之前的那点事儿系列
MVC之前的那点事儿系列,是笔者在2012年初阅读MVC3源码的时候整理的,主要讲述的是从HTTP请求道进入MVCHandler之前的内容,包括了原创,翻译,转载,整理等各类型文章,当然也参考了博客园 ...
- Android实现系统重新启动
有些Android版本没有系统重启的功能,非常不方便.需要我们自己开发一个能够重新启动的应用. 首先定义布局文件: <?xml version="1.0" encoding= ...
- HTML标签小结
HTML:超文本标记语言 超:超链接 超文本:超出文本(可加入图片,文字,音频视频播放器) 标记:标签 HTML文档 以<html...>开始 , 以</html> ...
- Android 亮度调节
最近在做一个App的设置项,亮度调节.真正做时,发现Android亮度调节比预想要复杂一些.其实目前网上已有不少这方面的资料,但有些博文具有一定误导性.在此将这块内容按照自己理解整理一下. 整体上看, ...
- SSH中的jar包讲解(1)
我们在搭建SSH框架的时候,需要引入各自的一些jar包,相信很多初学者跟我一样,搜个资料,照搬过来(当然版本还得对应),至于为什么要引入这些个jar包,引入它们的作用是啥子,一头雾水,今天我就来跟这些 ...
- Java的对象初始化过程
成员变量(字段)初始化顺序 在一个类里初始化的顺序是由成员变量在类里面的定义的顺序来决定的.即使成员变量大量散布于类的各个方法定义的中间,那些成员变量仍会在调用任何方法之前得以初始化,甚至在构造函数调 ...