Java BIO、NIO建立HTTP Server之对比

本文探讨了Java BIO与NIO的区别,包括读取方式、线程模型及适用场景,并通过具体案例分析了两者在文件上传服务器中的表现。最终得出结论,在高并发、小数据量场景下NIO更优,而在大IO如上传下载服务中BIO更为适合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前用Java的BIO在Android平台上建立了一个HTTP Server,可以上传和下载文件,但是感觉上交互的效率不是很高,
速度上不是很够,BIO的代码就不贴了,相似的实现网络上已经很多了。于是就研究了一段时间的NIO,简单说说NIO与BIO的不同之处吧:

BIO 是对字节进行读取,NIO 是对缓冲区进行读取
BIO 是一个连接一个线程,线程数多,NIO 则是一个请求一个线程,也就是有数据的时候才需要建立线程
BIO 适合大量数据传输,NIO 则适合高并发

使用NIO建立文件上传服务器遇到了很大的阻碍,不知道如何接受分片上传的文件,问题很大,如下是我遇到的一些问题

无法标识文件上传是否结束,因为前端是分片上传文件的,而HTTP 长连接会导致几个上传的都是同一个socket,异步上传中可能标识文件上传完成的报文比正常的文件报文还早发送到服务器,尤其是标识文件上传完成的报文的内容短,读取快
如果只新建一个ByteBuffer用于传输,那么只能适合于单线程的情况,但是这种情况容易导致部分报文传输时间过长则失败
对每个线程创建一个ByteBuffer则内存过大
NIO的每次read方法不一定能够读到所有的数据,需要多次读取

总而言之,NIO 适合做聊天服务器、游戏服务器等高并发、数据量小的情况,如果用在上传下载服务器这类大io的情况下,还是传统的BIO比较适合。

以下是一些有关于HTTP Server的文章,可以参考阅读:
HTTP Server漫谈
Java nio 建立HTTP Server
NanoHttpd 安卓HTTP Sever建立

### JavaBIONIO 和 AIO 的区别 #### 同步阻塞 I/O (BIO) 同步阻塞 I/O 是最传统的 I/O 模型。在这种模式下,当应用程序发起一个 I/O 请求时,调用线程会被挂起直到请求完成。这意味着如果当前线程正在处理某个耗时的 I/O 操作,则该线程无法继续执行其他任务。 对于服务器端开发而言,通常需要为每一个客户端连接创建一个新的线程来处理通信过程中的读写操作。这种方式虽然简单直观,但在高并发场景下的资源消耗非常大,因为每个活跃连接都需要占用独立的工作线程[^5]。 ```java ServerSocket server = new ServerSocket(port); while(true){ Socket client = server.accept(); // 此处会发生阻塞 } ``` #### 非阻塞 I/O (NIO) 非阻塞 I/O 则允许程序在发出 I/O 调用时不被阻塞,而是立即返回给调用方。具体来说,在 NIO 下可以通过 `Selector` 对象管理多个 Channel 实例的状态变化事件(如可读、可写)。一旦有就绪状态发生改变,就会触发相应的回调逻辑去处理实际的数据交换工作。这种机制大大提高了单个线程所能承载的最大并发量,并且降低了上下文切换带来的开销[^3]。 ```java // 创建 Selector 并注册感兴趣的事件类型 Selector selector = Selector.open(); serverChannel.configureBlocking(false); serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 处理已准备好的 channel int readyChannels = selector.select(); if (readyChannels == 0) continue; Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()){ SelectionKey key = keyIterator.next(); if(key.isAcceptable()) { // handle accept event... } else if (key.isReadable()) { // handle read event... } ... } ``` #### 异步非阻塞 I/O (AIO/NIO.2) 自JDK 7引入以来,AIO 提供了一个更加高级别的抽象层用于构建高效的网络服务端应用。不同于之前的两种方式,这里所有的I/O操作均采用完全异步的方式运行——既不会阻塞也不会轮询查询结果;相反地,它们会在后台由操作系统负责调度直至完成后通知用户态的应用代码。开发者只需定义好完成处理器(`CompletionHandler`)即可轻松捕获到最终的结果反馈信息[^1]. ```java AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open(); serverChannel.bind(new InetSocketAddress(port)); serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { @Override public void completed(AsynchronousSocketChannel result, Void attachment) { System.out.println("New connection accepted"); ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); result.read(buffer, buffer, new ReadCompletionHandler(result)); } @Override public void failed(Throwable exc, Void attachment) { logger.log(Level.SEVERE,"Failed to accept a connection",exc); } }); ``` ### 应用场景分析 - **低并发环境**: 如果业务需求较为单一且预计访问压力不大,那么可以选择简单的 BIO 方案快速搭建原型系统; - **中等规模并发负载**: 当面对一定数量级的同时在线用户数时,建议考虑基于 NIO 构建的服务架构,利用多路复用技术提升吞吐率; - **大规模分布式集群部署**: 在追求极致性能表现的情况下,尤其是涉及到海量短链接频繁交互或是长时间保持长连接通讯的任务场景,应该优先评估是否适合迁移到支持全异步语义的新一代 API —— 即 AIO 上面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值