一文读懂 BIO,NIO,AIO

1.是什么
 

1.1 啥是同步/异步,啥是阻塞/非阻塞
 

所谓同步和异步:

同步指协同步调,多个事物不能同时进行,必须一个一个地来,上一个事物结束后,下一个事物才开始。

  • 当一个事物正在进行时,其他事物通常处于一种“等待”的状态,因为后续事物需要依赖前一个事物的结果或资源。
  • 同步更关注宏观整体的逐个串行化关系,不太关注某个瞬间具体事物的状态。
  • 在资源少需求多的场景下,可以使用排队来实现同步,例如排队买火车票:售票大厅更在意的是旅客一个一个的到窗口去买票,因为一次只能卖一张票。
  • 存在由逻辑先后顺序导致的同步,例如先更新代码、再编译、接着打包。
  • 除了全局范围外,在某些关键点执行同步即可,例如食堂只有一个卖饭窗口:食堂只有一个卖饭窗口,肯定是同步的,一个人买完,下一个人再买。但吃饭的时候也是一个人吃完,下一个人才开始吃吗?当然不是啦。。
  • 小粒度的事物同步通常是天然支持的,而大粒度的事物同步往往需要手工处理,例如两个线程之间的同步需要手工处理。

异步指步调各异,多个事物可以同时进行,谁都不用管谁,所有的事物都在同时进行中。

  • 多个线程、方法、语句、CPU指令等都是多个事物。

注意事项

  • 需要权衡操作正确性和性能之间的关系,选择合适的同步方式。
  • 异步可以提高性能,但可能会导致操作顺序出现问题,需要额外处理来确保正确性。
  • 并发是指多个任务同时执行,而不是一个接一个地执行。
  • 并行是指多个任务在不同的处理器上同时执行,可以更大程度地提高系统的效率和吞吐量。

所谓阻塞和非阻塞:

阻塞和非阻塞是一种用于描述计算机程序在等待系统调用(例如输入/输出操作)完成时的行为方式。

假设你在餐馆点了一份菜,如何判断服务员是否处于阻塞或非阻塞状态呢?

  • 阻塞状态:服务员将你的订单交给厨房后,就一直站在那里等待,直到你的菜做好并上桌时才离开。这个过程中,服务员无法处理其他顾客的订单,只能等待当前订单完成。
  • 非阻塞状态:服务员将你的订单交给厨房后,可以离开去处理其他顾客的订单。每隔一段时间,他会回来询问一下你的菜是否已经做好。如果你的菜还没有做好,他会继续离开去处理其他的订单,直到你的菜准备好为止。这个过程中,服务员可以同时处理多个订单,不需要等待当前订单完成。

通俗来说,阻塞就像是你需要等待某些事情发生,期间无法进行其他的任务,就像服务员需要等待菜品做好;而非阻塞则类似于你在等待的过程中可以做其他的事情,比如看看菜单、聊天、喝水等,就像服务员可以同时处理多个订单。

所谓阻塞和等待:

等待是阻塞的一种表现,它表示随着时间流逝,没有任何有意义的事情发生或进行。阻塞的本质含义是,由于某些原因你关心的事情无法继续进行,因此需要等待。不过在等待期间,你可以做一些与其它事情无关的活动,并且这些活动并不影响你对相关事情的等待。在堵车时,人们可以选择干等、玩手机、和别人聊天、打牌、吃饭等活动,因为这些活动并不影响他们对堵车的等待,但是他们的车必须呆在原地。在计算机中,一般选择干等阻塞的线程,因为这是最容易实现的方法,只需挂起线程并让出CPU。当条件满足时,该线程将被重新调度。

所谓阻塞IO和非阻塞IO:

阻塞IO和非阻塞IO最直观的区别就是在读写数据时,是否会阻塞当前线程。以网络通信为例:

  • 阻塞IO模型:假设你要从另一台计算机上下载一个800MB的视频文件,使用阻塞IO模型的话,你必须等待整个文件的下载完成后才能进行其他操作,期间你不能进行任何其他的任务。
  • 非阻塞IO模型:使用非阻塞IO模型的话,你可以开启一个下载线程,然后继续进行其他的工作,同时每隔一段时间就检查一下下载进度,如果下载完成了,则可以对已经下载好的部分进行处理,而不用等到整个文件都下载完毕。

通俗来说,阻塞IO就像你去饭店吃饭,点完菜后必须等菜做好才能吃,期间你无法做其他事情;而非阻塞IO则类似于你在家里做饭,同时可以继续干其他的事情,每隔一段时间就去看看菜有没有煮好,可以及时处理已经煮好的食材。

所谓同步IO和同步阻塞IO:

同步IO和同步阻塞IO的区别在于,在读写数据时,是否会一直等待IO操作完成。

  • 同步IO模型:假如你要煮一锅粥,使用同步IO模型的话,你需要不断地看着锅里的米粒,不停地搅拌和加水,直到粥煮好为止。这个过程中,你必须全程参与,并且不能进行其他的任务。
  • 同步阻塞IO模型:如果使用同步阻塞IO模型,你可以把米放进锅里,然后开启一个计时器,每隔一段时间就检查一下锅里的状态,如果发现粥已经煮好了,则可以关火,拿起勺子处理粥了。期间你可以做一些其他的事情,但是你必须时刻关注锅里的状态,否则粥就可能煮焦或者煮干。

通俗来说,同步IO就像你亲自做饭,需要全程参与并不断关注,不能做其他事情;而同步阻塞IO则类似于你用电饭煲煮饭,只需要按下按钮,然后每隔一段时间看看饭煮好没有,期间可以做其他的事情。

所谓异步IO和异步阻塞/非阻塞IO:

异步IO和异步阻塞/非阻塞IO的区别在于,在读写数据时,是否需要等待IO操作完成。

  • 异步IO模型:假设你要做一个蛋糕,使用异步IO模型的话,你可以将所有需要的材料都准备好,并交给一个厨师处理。这个过程中,你无需关注具体的制作流程,只需要在需要时接收制作进度或者最终的成品即可。
  • 异步阻塞/非阻塞IO模型:如果使用异步阻塞/非阻塞IO模型,你可以把蛋糕送到烘培店里面进行制作,并告诉店员你需要多久时间内拿到成品。然后你可以回家干其他的事情,当时间到达时再返回烘培店去取蛋糕。在此期间,你无需关心具体的烘培流程,只需要在需要时去取蛋糕即可。

通俗来说,异步IO就像你委托别人做蛋糕,只需要在需要时接收成果即可;而异步阻塞/非阻塞IO则类似于你把蛋糕交给烘培店,告诉他们何时需要取蛋糕,然后你可以做一些其他的事情。异步阻塞IO模型需要等待操作完成后才能返回结果,而异步非阻塞IO模型则不会阻塞当前线程,可以在数据准备好时立即返回。

所谓多路复用IO:

多路复用IO是指在一个线程中同时处理多个I/O连接,以提高系统的并发性和吞吐量。通俗来说,多路复用IO就像一个服务员通过一张订单同时为多桌顾客服务。

假设有这样一个场景:一个餐厅里有10张桌子需要点餐,如果只有一个服务员,他必须一个一个地去每张桌子上取订单,再回去提交给厨师进行制作,然后再把菜品送到对应的桌子上。这个过程中,服务员需要不断地在桌子之间穿梭,耗费大量的时间和精力。

而如果采用多路复用IO模型,就相当于在餐厅里增加了几个服务员,每个服务员可以同时处理多张桌子的订单。具体实现方式是,服务员在每张桌子上留下电话,接收顾客的点单,并将订单交给厨师进行制作。这样,服务员就无需在桌子之间来回奔波,而是可以通过统一的电话中心来处理所有的订单,使得处理效率大大提高。

在计算机领域中,多路复用IO也类似于这个例子,能够在一个线程中同时处理多个I/O操作,从而提高系统的处理能力和效率。此外,多路复用IO还可以减少线程的创建和上下文切换,降低系统的负担和开销。

总结:

同步阻塞(BIO):

IO的同步阻塞是指在进行输入/输出操作时,如果当前线程请求的数据还没有到达或者还没有准备好,线程就会进入等待状态,直到数据准备好并成功读取或写入到目标位置为止。

举个例子来说,假设你要从USB闪存盘中复制一个1GB的文件到电脑硬盘上,使用同步阻塞IO模型的话,你需要等待整个文件全部复制完成后才能进行其他操作。这个过程中,你无法进行其他任务,电脑也不能访问USB驱动器。而且如果发生阻塞,CPU也会一直运行,占用大量的资源,整个系统的效率和性能都会受到影响。

在餐馆就餐的情况下,同步阻塞IO可以类比为你点菜后需要等待菜品做好才能享用,期间你无法进行其他操作。此外,如果厨房出现拥堵或者菜品出现问题,就会导致整个服务过程被阻塞,直到问题得到解决并重新开始为止。

同步非阻塞(NIO):

IO的同步非阻塞是指在进行输入/输出操作时,如果当前线程请求的数据还没有准备好,则不会进入等待状态,而是立即返回一个错误代码或者空值,让线程可以继续执行其他任务。

举个例子来说,假设你要从云端下载一个1GB的文件到本地电脑上,使用同步非阻塞IO模型的话,你可以先发起下载请求,然后就可以去做其他的事情了,比如写文档、打电话等。当下载完成后,系统会通知你,然后你就可以对已下载好的数据进行读取和处理了。在这个过程中,你无需等待整个文件全部下载完成,也无需一直占用CPU资源,系统效率得到了提高。

在餐馆就餐的情况下,同步非阻塞IO可以类比为你点菜后,服务员告诉你需要等待一段时间才能上菜,但是你可以先去做其他的事情,比如逛逛商场或者读读书。当菜品准备好后,服务员会通知你,然后你就可以前往餐桌享用美食了。这样既不会浪费时间,也不会影响其他的任务。

异步阻塞:

IO的异步阻塞是指在进行输入/输出操作时,当前线程不必等待IO操作完成,而是可以继续执行其他任务。但是,在数据准备好之前,线程仍然会被阻塞,无法处理其他的I/O请求。

举个例子来说,假设你要从一个Web服务器上下载一个1GB的文件。使用异步阻塞IO模型的话,你可以发起下载请求并继续进行其他任务。当服务器将数据传输到客户端后,系统会通知你,然后你就可以对已传输好的数据进行读取和处理了。在这个过程中,你不必等待整个文件全部下载完成,但是系统仍然需要阻塞当前线程,等待数据传输完成才能进行下一步操作。

在餐馆就餐的情况下,异步阻塞IO可以类比为你点菜后,服务员告诉你需要等待一段时间才能上菜,但是你可以先去做其他的事情,比如逛逛商场或者读读书。当菜品准备好后,服务员会通知你,然后你就可以前往餐桌享用美食了。不过,在此期间,服务员还是需要为你准备菜品,并等待厨房确认,这个过程可能会消耗一些时间,也会占用一定的系统资源。

异步非阻塞(AIO):

IO的异步非阻塞是指在进行输入/输出操作时,当前线程不必等待IO操作完成,也不会被阻塞,而是可以继续执行其他任务。当数据准备好之后,系统会通过回调函数通知线程进行后续处理。

举个例子来说,假设你在使用一个基于事件驱动的Web服务器,需要从服务器上下载一个1GB的文件。使用异步非阻塞IO模型的话,你可以发起下载请求并继续进行其他任务。当服务器将数据传输到客户端后,系统会通过回调函数通知你,然后你就可以对已传输好的数据进行读取和处理了。在这个过程中,你不必等待整个文件全部下载完成,也不会占用大量的系统资源。

在餐馆就餐的情况下,异步非阻塞IO可以类比为你点菜后,服务员告诉你需要等待一段时间才能上菜,但是你可以先去做其他的事情,比如逛逛商场或者读读书。当菜品准备好后,服务员通过广播或者电话通知你,然后你就可以前往餐桌享用美食了。在此期间,服务员可以为其他顾客服务,不会影响系统效率和性能。


1.2 啥是AIO,NIO,BIO

1.2.1 AIO

Aio (Asynchronous I/O) 是一种异步非阻塞IO模型,它可以在进行输入/输出操作时,当前线程可以继续执行其他任务,不必等待IO操作完成。同时,在数据准备好之后,系统会通过回调函数通知线程进行后续处理。AIO 也就是 NIO 2。Java 7 中引入了 NIO 的改进版 NIO 2,它是异步 IO 模型。目前来说 AIO 的应用还不是很广泛。Netty 之前也尝试使用过 AIO,不过又放弃了。这是因为,Netty 使用了 AIO 之后,在 Linux 系统上的性能并没有多少提升。

举个例子来说,假设你使用一个支持Aio的Web服务器并需要从服务器上下载一个1GB的文件。使用Aio模型的话,你可以发起下载请求并继续进行其他任务,例如写代码或者发送电子邮件。当服务器将数据传输到客户端后,系统会通过回调函数通知你,然后你就可以对已传输好的数据进行读取和处理了。在这个过程中,你不必等待整个文件全部下载完成,也不会占用大量的系统资源,因为Aio模型是异步非阻塞的。

在餐馆就餐的情况下,Aio可以类比为你点菜后,服务员告诉你需要等待一段时间才能上菜,但是你可以先去做其他的事情,比如逛逛商场或者读读书。当菜品准备好后,服务员通过广播或者电话调用回调函数通知你,然后你就可以前往餐桌享用美食了。在此期间,服务员可以为其他顾客服务,不会影响系统效率和性能。

1.2.2  BIO

Bio 是一种同步阻塞IO模型,它在进行输入/输出操作时,当前线程需要等待数据准备好并成功读取或写入到目标位置为止,期间无法进行其他任务。

举个例子来说,假设你从一个USB闪存盘中复制一个1GB的文件到电脑硬盘上。使用Bio模型的话,你需要等待整个文件全部复制完成后才能进行其他操作。这个过程中,你无法进行其他任务,电脑也不能访问USB驱动器。而且如果发生阻塞,CPU也会一直运行,占用大量的资源,整个系统的效率和性能都会受到影响。

在餐馆就餐的情况下,Bio可以类比为你点菜后需要等待菜品做好才能享用,期间你无法进行其他操作。此外,如果厨房出现拥堵或者菜品出现问题,就会导致整个服务过程被阻塞,直到问题得到解决并重新开始为止。

1.2.3 NIO

Nio (Non-blocking I/O) 是一种异步非阻塞IO模型,它在进行输入/输出操作时,当前线程不需要等待IO操作完成,也不会被阻塞,而是可以继续执行其他任务。当数据准备好之后,系统会通过回调函数通知线程进行后续处理。

举个例子来说,假设你要从一个Web服务器上下载一个1GB的文件。使用Nio模型的话,你可以发起下载请求并继续进行其他任务。当服务器将数据传输到客户端后,系统会通过回调函数通知你,然后你就可以对已传输好的数据进行读取和处理了。在这个过程中,你不必等待整个文件全部下载完成,也不会占用大量的系统资源。

在餐馆就餐的情况下,Nio可以类比为你点菜后,服务员告诉你需要等待一段时间才能上菜,但是你可以先去做其他的事情,比如逛逛商场或者读读书。当菜品准备好后,服务员通过广播或者电话通知你,然后你就可以前往餐桌享用美食了。在此期间,服务员可以为其他顾客服务,不会影响系统效率和性能。

1.2.4  AIO, BIO,NIO对比
 

I/O                     数据传输         时间        实现方式                   使用场景                                                                                应用
BIO                    Stream            JDK1        Socket                      连接数目比较小且固定的架构                                               一般很少使用
NIO                  ByteBuffer        JDK4        Channel Selector      连接数目多且连接比较短(轻操作)的架构                       网络框架netty
AIO                 ByteBuffer         JDK7        Channel                    连接数目多且连接比较长(重操作)的架构                        Ngix,MySQL新版本

2. 为什么


2.1 java 为什么使用BIO

Java中的BIO 是一种同步阻塞式I/O模型,它的实现方式是在发起I/O请求时,如果当前没有数据可读或写入,则阻塞线程等待数据到来或者完成写入操作。之所以在某些情况下需要使用BIO,是因为BIO具有以下特点:

  1. 简单易用:BIO使用起来非常简单,可以快速地实现一个服务器端或客户端,适用于I/O负载不高的情况。
  2. 可靠稳定:由于BIO在I/O操作时会进行阻塞,因此可以保证数据传输的稳定性和可靠性。
  3. 兼容性好:BIO支持所有的传输协议,包括TCP/IP、UDP等,且无需进行复杂的配置。

虽然BIO具有以上优点,但是其缺点也比较明显:

  1. 阻塞问题:BIO在进行I/O操作时会阻塞线程,导致资源浪费,I/O并发度低。
  2. 可扩展性差:BIO采用一对一的方式进行I/O处理,当连接数较大时,服务器的线程数量也会随之增加,这样会导致系统的性能急剧下降。
  3. 性能瓶颈:受限于硬件性能,BIO的性能瓶颈明显,不适用于高并发场景。

因此,在高并发和I/O负载较高的情况下,一般不建议使用BIO,而应该采用NIO(Non-blocking I/O)或AIO(Asynchronous I/O)等更高效的I/O模型。

虽然BIO模型的性能不如NIO或AIO,但是在某些情况下还是有一定优势的。下面列出了一些使用BIO模型的情形:

  1. 需要处理的数据量较小。这种情况下,使用BIO模型可以更简单和易于管理。

  2. 系统负载较低。在负载较低的情况下,BIO模型的开销相对较小,且CPU资源利用率也更高。

  3. 需要支持多个客户端连接的场景比较少。在这种情况下,使用BIO模型可以更容易实现和维护,因为线程的数量不会很大,而且同步阻塞的特性可以避免竞争条件等问题。

总之,使用BIO模型适用于一些简单的、需要低延迟、并发请求不高的应用场景。但是对于高并发、高负载、需要快速响应的应用程序来说,推荐使用更高效的异步非阻塞IO模型,如NIO或AIO。

2.1 netty 为什么使用NIO

Netty使用NIO的原因有以下几点:

  1. 高性能: NIO与BIO相比,可以支持更高的并发连接数和更快的数据传输速度。因为使用了异步非阻塞的I/O模型,可以极大地提高网络通信的效率和吞吐量。

  2. 低内存消耗: NIO在处理大量并发连接时,使用较少的线程数量,减少上下文切换的开销,从而避免了过多线程导致的内存消耗。

  3. 可扩展性: Netty基于NIO的异步非阻塞模型,允许多个请求共享一个线程池,提高系统的并发处理能力,并且可以简单地水平扩展集群来满足不断增长的业务需求。

  4. 稳定性: 在使用NIO时,当某个连接出现异常时,只会影响到该连接,而不会影响整个应用程序或其他连接的正常运行。

  5. 跨平台: NIO库是Java标准库的一部分,因此可以跨平台使用,不需要像操作系统特定的API一样编写不同的代码。

综上所述,使用NIO可以提高系统的性能、可伸缩性和稳定性,同时具有跨平台的优势。这就是为什么Netty选择使用NIO模型。

2.3 nginx为啥使用AIO

Nginx使用AIO的原因有以下几点:

  1. 高性能: AIO模型将I/O操作交给系统内核处理,可以减少用户态和内核态之间的切换次数,从而提高了I/O操作的效率和响应速度。

  2. 低资源消耗: AIO模型可以使用较少的线程来处理大量I/O请求,减少了线程上下文切换的开销,同时避免了过多线程导致的内存消耗。

  3. 易于管理: 使用AIO模型可以更好地利用系统资源,降低运维成本。在高并发场景下,使用AIO模型可以更轻松地实现负载均衡、故障转移等功能。

  4. 可伸缩性: AIO模型可以支持更高的并发连接数,并且可以简单地水平扩展集群来满足不断增长的业务需求。

  5. 跨平台: AIO库也是Java标准库的一部分,因此可以跨平台使用,不需要像操作系统特定的API一样编写不同的代码。

综上所述,使用AIO可以提高系统的性能、可伸缩性和稳定性,同时具有跨平台的优势。这就是为什么Nginx选择使用AIO模型。

3.怎么实现

3.1 AIO 使用demo

服务端:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class Server {

    public static void main(String[] args) throws IOException {
        AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
        server.bind(new InetSocketAddress(8888));
        System.out.println("Listening on port 8888...");

        while (true) {
            server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
                @Override
                public void completed(AsynchronousSocketChannel channel, Void attachment) {
                    try {
                        System.out.println("Accepted connection from " + channel.getRemoteAddress());
                        String message = "Hello, welcome to the server!";
                        ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
                        channel.write(buffer, null, new CompletionHandler<Integer, Void>() {
                            @Override
                            public void completed(Integer result, Void attachment) {
                                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                                channel.read(readBuffer, readBuffer, new EchoHandler(channel));
                            }

                            @Override
                            public void failed(Throwable exc, Void attachment) {
                                System.out.println("Write failed: " + exc);
                                try {
                                    channel.close();
                                } catch (IOException e) {
                                  
                                }
                            }
                        });
                    } catch (IOException ex) {
                        System.out.println("Accept failed: " + ex);
                    }
                }

                @Override
                public void failed(Throwable exc, Void attachment) {
                    System.out.println("Accept failed: " + exc);
                }
            });
        }
    }
}

class EchoHandler implements CompletionHandler<Integer, ByteBuffer> {
    private AsynchronousSocketChannel channel;

    public EchoHandler(AsynchronousSocketChannel channel) {
        this.channel = channel;
    }

    @Override
    public void completed(Integer result, ByteBuffer buffer) {
        if (result == -1) {
            try {
                channel.close();
            } catch (IOException ex) {
             
            }
            return;
        }

        buffer.flip();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        String message = new String(bytes);

        System.out.println("Received message from " + channel.getRemoteAddress() + ": " + message);

        ByteBuffer writeBuffer = ByteBuffer.wrap(bytes);
        channel.write(writeBuffer, null, this);
    }

    @Override
    public void failed(Throwable ex, ByteBuffer buffer) {
        System.out.println("Echo failed: " + ex);
        try {
            channel.close();
        } catch (IOException e) {
         
        }
    }
}

客户端:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;

public class Client {

    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
client.connect(new InetSocketAddress("localhost", 8888)).get();
ByteBuffer buffer = ByteBuffer.allocate(1024);
//自己把 client  加上,文章发布检查问题
client.read(buffer).get();
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes);
System.out.println("Received message from server: " + message);

client.close();  
  }
}

3.2 NIO 使用demo

服务端:

import java.io.IOException;
import java.net.InetSocketAddress;
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.Iterator;

public class Server {

    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("Listening on port 8888...");

        while (true) {
            int readyChannels = selector.select();

            if (readyChannels == 0) {
                continue;
            }

            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();

            while (iter.hasNext()) {
                SelectionKey key = iter.next();

                if (key.isAcceptable()) {
                    SocketChannel clientChannel = serverSocketChannel.accept();
                    clientChannel.configureBlocking(false);
                    clientChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("Accepted connection from " + clientChannel.getRemoteAddress());
                } else if (key.isReadable()) {
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    clientChannel.read(buffer);
                    buffer.flip();
                    byte[] bytes = new byte[buffer.remaining()];
                    buffer.get(bytes);
                    String message = new String(bytes);

                    System.out.println("Received message from " + clientChannel.getRemoteAddress() + ": " + message);

                    clientChannel.write(ByteBuffer.wrap(("Echo: " + message).getBytes()));
                }
                iter.remove();
            }
        }
    }
}

客户端:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class Client {

    public static void main(String[] args) throws IOException {
        SocketChannel client = SocketChannel.open();
        client.connect(new InetSocketAddress("localhost", 8888));

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("Hello, server!".getBytes());
        buffer.flip();
        client.write(buffer);

        buffer.clear();
        client.read(buffer);
        buffer.flip();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        String message = new String(bytes);

        System.out.println("Received message from server: " + message);

        client.close();
    }
}

3.3 NIO 使用demo

服务端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("Listening on port 8888...");

        while (true) {
            Socket clientSocket = serverSocket.accept();
            System.out.println("Accepted connection from " + clientSocket.getRemoteSocketAddress());

            InputStream input = clientSocket.getInputStream();
            OutputStream output = clientSocket.getOutputStream();

            byte[] buffer = new byte[1024];
            int len = input.read(buffer);

            String message = new String(buffer, 0, len);
            System.out.println("Received message from " + clientSocket.getRemoteSocketAddress() + ": " + message);

            output.write(("Echo: " + message).getBytes());
            output.flush();

            clientSocket.close();
        }
    }
}

客户端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Client {

    public static void main(String[] args) throws IOException {
        Socket clientSocket = new Socket("localhost", 8888);

        OutputStream output = clientSocket.getOutputStream();
        output.write("Hello, server!".getBytes());
        output.flush();

        InputStream input = clientSocket.getInputStream();
        byte[] buffer = new byte[1024];
        int len = input.read(buffer);

        String message = new String(buffer, 0, len);
        System.out.println("Received message from server: " + message);

        clientSocket.close();
    }
}

以下是一个使用Java BIO模型实现的简单请求百度接口的示例。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class HttpClient {

    public static void main(String[] args) throws IOException {
        String host = "www.baidu.com";
        int port = 80;
        String path = "/";

        Socket socket = new Socket(host, port);

        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        out.println("GET " + path + " HTTP/1.1");
        out.println("Host: " + host);
        out.println();

        String responseLine;
        while ((responseLine = in.readLine()) != null) {
            System.out.println(responseLine);
        }

        socket.close();
    }
}

4.总结

    综上所述,NIO,BIO,AIO,各有千秋,需结合应应用场景使用

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值