Netty,认识和深入(二),BIO

Netty,认识和深入(二),BIO,第1张

Netty,认识和深入(二),BIO BIO

JAVA BIO(blocking I/O):同步并阻塞(传统阻塞型),服务器实现模型为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制完善(实现多个客户连接服务器)

应用场景

BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序简单易理解。

BIO工作原理大致流程图如下:

    服务器启动一个ServreSocket;客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通讯;客户端发出请求后,先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝;如果有响应,客户端线程会等待请求结束后,在继续执行;
JAVA代码示例:
package com.kelecc.bio;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;



public class BIOServer {

    public static void main(String[] args) {
        //创建一个线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        try {
            //创建ServerSocket ,监听8888端口
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("服务启动");
            while(true){
                //监听,等待客户端链接,该方法阻塞,直到建立连接。
                System.out.println("等待连接");
                //main方法的主线程
                System.out.println("线程信息 id = " + Thread.currentThread().getId());
                System.out.println("线程名字 name="+Thread.currentThread().getName());
                final Socket socket = serverSocket.accept();
                System.out.println("连接到客户端");
                //创建一个线程,与之通讯
                 executorService.execute(()->{
                     handler(socket);
                 });
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void handler(Socket socket){
       byte[] bytes  = new byte[1024];
        try {
            //得到输入流
            InputStream inputStream = socket.getInputStream();
            //循环读取客户端发送的数据
            while(true){
                System.out.println("线程信息 id = " + Thread.currentThread().getId());
                System.out.println("线程名字 name="+Thread.currentThread().getName());
                System.out.println("read.......");
                int read = inputStream.read(bytes);
                if(read != -1){
                    //控制台打印输出
                    System.out.println("输出客户端发送的数据"+new String(bytes,0,read));
                }else{
                    //读取完毕
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            System.out.println("关闭客户端连接");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

代码运行:

只要没有客户端连接,线程都会阻塞,socket.accept 调用之前:

final Socket socket = serverSocket.accept();

使用CMD 创建一个客户端连接:使用命令测试8888端口

telnet 127.0.0.1 8888

连接成功:

控制台打印:

观察打印输出,我们发现:

主线程main , 在有客户端连接过后,又会阻塞在 客户端连接进来之前,所以这里会打印 两个等待连接;我们也可以看上面的流程图 一目了然;

serverSocket.accept();

而我们客户端连接的子线程 通过我们设计代码连接成功后,当我们没有发送数据,子线程会阻塞在 read/write这个 *** 作时;可以看上面的流程图;

int read = inputStream.read(bytes); 

发送数据,在当前界面,按住 ctrl+] 键;


通过 send 发送数据;

观测输出日志;

发送数据成功之后,子线程依旧阻塞在 read *** 作之前

接下来,我们新建第二个客户端,启动第二个cmd,观察输出日志;

总结:

每个请求都需要创建独立的线程,与对应的客户端进行数据Read,业务处理,数据Write;当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大;连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在Read *** 作上,造成线程资源浪费;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5719835.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存