前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java网络编程之BIO

Java网络编程之BIO

作者头像
心平气和
发布2021-03-16 16:31:18
3660
发布2021-03-16 16:31:18
举报
文章被收录于专栏:程序员升级之路

一、网络编程模型介绍

作为一个WEB程序员,网络编程模型是我们需要掌握的基础知识,在介绍具体模型之前先聊下几个基本的概念:

1.1 基本概念

阻塞和非阻塞

这里说的阻塞指调用者调用一个函数的时候,如果这个函数不能马上返回,则调用者会一直等函数返回才能往下执行其它代码;

而非阻塞则是反过来,如果函数不能马上返回,调用者线程往下执行其它代码,不过需要自己询问前面的调用结果。

打个比方,在餐厅中,服务员A现在在为一位客人C点菜,如果C还没点好菜之前A不能为其他客人点菜就是阻塞的,如果可以则是非阻塞的。

同步和异步

同步:调用者调用某个函数时,必须等待结果才能返回。

异步:调用者调用某个函数,如果函数不是马上返回,调用者可以往下执行,并且函数返回的时候会主动通知调用者。

还是回到上面的例子,A为C点菜的时候,如果不做其它的事,则是同步的,如果可以做其他的事,并且C点菜完了再叫A,则称为异步的。

实际情况有的可能是一起的,像同步阻塞,网上有的也说要分调用者和被调用者,这里我们不准备做这方面的深入讨论,大家大概明白这回事就行,异步和非阻塞我们先简单的理解为大家都不等,但是异步是前面的任务完成了主动通知我,而非阻塞需要自己实现查询。

1.2 网络编程模型

接下来说下网络模型,这里涉及到内核和用户态的概念了,前提是我们已经了解这些概念。

具体的模型大概分为以下几种类型:

A、阻塞I/O模型

在用户线程发出请求后,内核会检查数据是否就绪,此时用户线程会一直阻塞等待数据就绪,数据就绪后,内核将数据复制到用户线程空间中,并将用户线程恢复成可执行的,此时用户线程将解除阻塞状态并开始处理数据。

  • B、非阻塞I/O模型

用户线程在发起一个I/O操作,无需阻塞便可以马上得到内核返回的结果。如果内核的返回数据为空,则表示内核还没准备好数据,需要稍后再次发起I/O请求。一旦内核中数据准备好,并且再次收到用户线程的请求,内核就会立刻将数据复制到用户线程,用户线程下次发起查询的时候就可以得到数据。

  • C、多路复用I/O 模型

是多线程并发编程用的较多的模型,通过在一个Selector线程上以轮训方式检测在多个Socket上是否有多个事件到达,并逐个进行事件处理和响应。

这种模型下Selector线程可能会成为性能瓶颈,导致后续的事件迟迟的得不到响应。在实际应用中多路复用建议只做数据的接收和转发,将具体的业务操作以及复杂的逻辑运算转发给后面的业务线程处理。

D、信号驱动I/O模型

用户线程发起一个I/O请求,系统会为该请求对应的Socket注册一个信号函数,然后用户线程可以继续执行其他业务逻辑,在内核数据就绪时,系统发送一个信号到用户线程,用户线程在收到信号后,会在信号函数中调用对应的I/O读写操作来完成实际的I/O请求操作。

E、异步I/O模型

在该模型中,用户线程会发起一个请求到内核后,内核会立即返回一个状态,来说明此时的请求是否成功发起,在这个过程中用户线程不会发生任何的阻塞;

接着内核会等待数据准备完成并将数据复制到用户线程中,复制完后内核发送一个信号到用户线程,通知用户线程操作完成。

这个模型和信号驱动的区别是:信号驱动I/O由内核通知我们何时可以开始I/O操作,而异步I/O模型则由内核通知我们I/O什么时候完成。

二、Java中对应模型实现

1、同步阻塞I/O(BIO)

最古老的模型,服务端创建ServerSocket,然后绑定监听端口,然后不断的accept,然后处理具体一个个的Socket连接;客户端创建Socket,然后不断的发送/接收数据就可以了。

2、非阻塞I/O(NIO)

有的人也称New I/O,NIO提供了SocketChannel和ServiceSocketChannel不同套接字实现,这个后面专门细述。

3、异步I/O(AIO)

NIO2.0引入新的异步通道的概念,并提供文件和套接字的异步实现,这里也不详述。

如果写过C语言的就应该对这些有些了解,最早的Select,然后是Epoll,最终的原理是一样的,不过在Java层面的一些概念可能不一样。

三、BIO示例

下面写个简单的demo来了解BIO编程的过程:

1、服务端

代码语言:javascript
复制
public class EchoServer{

    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
    int port = 9001;

    ServerSocket server = null;
    try {
      server = new ServerSocket(port);
      Socket socket = null;
      while (true) {
          socket = server.accept();
          new Thread(new EchoServerHandler(socket)).start();
      }
     } finally {
        if (server != null) {
          server.close();
          server = null;
      }
    }
  }
}

具体步骤如下:

先创建ServerSocket;

不断的Accept,然后创建新线程去处理新进来的连接;

具体处理逻辑类:

代码语言:javascript
复制
public class EchoServerHandler implements Runnable {

    private Socket socket;

    public EchoServerHandler(Socket socket) {
      this.socket = socket;
    }

  
    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(
        this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String currentTime = null;
            String body = null;
            while (true) {
                body = in.readLine();
                if (body == null)
                    break;
                out.println(body);
            }
  } catch (Exception e) {
    if (in != null) {
      try {
          in.close();
      } catch (IOException e1) {
          e1.printStackTrace();
      }
    }
    if (out != null) {
        out.close();
        out = null;
     }
     if (this.socket != null) {
        try {
            this.socket.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        this.socket = null;
      }
  }

再看客户端代码

代码语言:javascript
复制
public class EchoClient {

  /**
   * @param args
   */
  public static void main(String[] args) {

    for (int i = 0; i < 10; i++) {
      run(args);
    }
  }

  private static void run(String[] args) {
    int port = 9001;

    Socket socket = null;
    BufferedReader in = null;
    PrintWriter out = null;
    try {
      socket = new Socket("127.0.0.1", port);
      in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      out = new PrintWriter(socket.getOutputStream(), true);

      out.println("hello world");
      String resp = in.readLine();
      System.out.println("response is : " + resp);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (out != null) {
        out.close();
        out = null;
      }

      if (in != null) {
        try {
          in.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
        in = null;
      }

      if (socket != null) {
        try {
          socket.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
        socket = null;
      }
    }
  }
}

客户端代码编写步骤如下:

1、创建Socket,注意这里要指定服务器的IP和端口号;

2、获取Socket的输入输出流;

3、根据情况对输出流写数据,或从输入流读数据。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员升级之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档