同步阻塞式IO模型-单线程版本

大家儿童节快乐,我是小黑。

今天和大家分享网络编程中的阻塞式IO模型。网络编程中有5钟常见的IO模型:同步阻塞式IO模型、同步非阻塞式IO模型、多路复用IO模型、信号驱动IO模型和异步IO模型。这篇博客暂时先分享阻塞式IO模型,其他的IO模型会在后续的博客里面持续更新。

什么是同步阻塞式IO模型

网络传输中,数据通过计算机网络传输到机器,直到程序读取到数据,会经历两个步骤:

  • 数据从网卡拷贝到内核空间
  • 数据从内核空间拷贝到用户空间,供应用程序读取

各种IO模型的区别就在于这两个步骤的方式不一样,同步阻塞式IO的做法是:用户线程发起 read() 调用后就阻塞了,让出CPU。内核等待网卡数据到来,把数据从网卡拷贝到内核空间,接着把数据拷贝到用户空间,再把用户线程唤醒。整个过程都是阻塞住的,如图所示。
image.png

单线程同步阻塞式IO模型的实现

这里实现一个最简单的单线程版本阻塞式IO模型,但麻雀虽小五脏俱全。

示例是一个Echo服务器,客户端发送什么数据,服务端就在加上一个日期然后原样返回。

我们先看看服务端怎么实现,代码如下。主体是一个死循环,阻塞在 accept() 方法上,有连接建立才会返回。返回后会调用 handle() 方法进行处理,一般都分为三个大步骤:读取客户端数据;处理客户端数据;结果返回给客户端。

public class EchoServer {

    private ServerSocket serverSocket;

    public static void main(String[] args) throws IOException {
        new EchoServer().start();
    }

    public EchoServer() throws IOException {
        serverSocket = new ServerSocket(8001); // 初始化一个ServerSocket,绑定8001端口
    }

    public void start() {

        for (; ; ) {
            Socket socket = null;
            try {
                socket = serverSocket.accept(); // 接收到一个客户端的连接
                handle(socket); // 进行处理,主要的步骤是:read->process->write
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void handle(Socket socket) {
        BufferedReader bufferedReader = null;
        PrintWriter printWriter = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            printWriter = new PrintWriter(socket.getOutputStream(), true);
            String line = null;
            while ((line = bufferedReader.readLine()) != null) { // read 读取客户端数据
                String echoMsg = new Date() + ": " + line; // process 处理客户端数据
                printWriter.println(echoMsg); // write 结果返回给客户端
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (Objects.nonNull(printWriter)) {
                    printWriter.close();
                }
                if (Objects.nonNull(bufferedReader)) {
                    bufferedReader.close();
                }
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
复制代码

再看看客户端代码。首先和服务端连接,然后读取控制台的数据发送到服务端,并等待服务端响应。

public class EchoClient {

    private Socket socket;

    public EchoClient() {
        socket = new Socket();
    }

    public static void main(String[] args) throws IOException {
        new EchoClient().start();
    }

    public void start() throws IOException {


        socket.connect(new InetSocketAddress("localhost", 8001)); // 建立连接
        try {
            BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
            InputStream in = socket.getInputStream();
            OutputStream out = socket.getOutputStream();
            BufferedReader bufReader = new BufferedReader(new InputStreamReader(in));
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(out), true);
            String msg = null;
            while ((msg = localReader.readLine()) != null) {
                printWriter.println(msg); // 发送数据
                System.out.println(bufReader.readLine()); // 接受响应
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (Objects.isNull(socket)) {
                socket.close();
            }
        }
    }
}

复制代码

跑起来的示例如下,绿色字体是客户端在控制台输入的数据,黑色字体是服务端响应的数据。

image.png

单线程版本的同步阻塞式IO模型有哪些问题

我们启动一个EchoServer,然后并行启动多个EchoClient,就能发现单线程版本的同步阻塞式IO模型都会有哪些问题。EchoServer只有一个线程,这个线程要独自一人读取客户端数据(read),进行业务处理(process),把响应发回给客户端(write)。中间任何一个步骤都可能会阻塞或者占用大块的CPU时间,如果连接一多,整个服务器势必会阻塞住很多客户端的连接,影响通信性能。

image.png

现实中,有许多实际应用要求服务器具有同时为多个客户端提供服务的能力,比如我们每天都在用的HTTP服务器,如果因为客户端过多造成长时间等待,会使得网站失去信誉,从而降低访问量。所以,单线程版本的同步阻塞式IO模型的问题在于:它是单线程的,不能充分发挥现在机器多线程的能力,无法支持并行处理多个用户请求的场景。

想要解决这个问题,就要用多线程,详情见下一篇:同步阻塞式IO模型-多线程版本,马上更新。

白白。

Ref

  • 极客时间:《深入拆解Tomcat&Jetty》
  • 孙卫琴:《Java网络编程精解》
  • Budi Kurniawan、Paul Deck:《深入剖析Tomcat》
  • 葛一鸣:《实战Java高并发程序设计》
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享