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

Java网络编程——BIO阻塞IO

作者头像
DannyHoo
发布2022-08-04 18:32:59
2770
发布2022-08-04 18:32:59
举报
文章被收录于专栏:Danny的专栏Danny的专栏

BIO(Blocking IO)也就是阻塞IO,当服务端和客户端交互时,如果服务端接收了一个客户端请求,就要为这个客户端一直服务直到结束,否则无法为下一个客户端服务。BIO就属于同步阻塞IO。

BIO单线程处理请求

BIO服务器端:

代码语言:javascript
复制
@Slf4j
public class BIOServer {
    @SneakyThrows
    public static void main(String[] args) {
        ServerSocket serverSocket=new ServerSocket();
        try {
            serverSocket.bind(new InetSocketAddress("127.0.0.1",8080),50);
            log.info("server started.");
            while (true){
                Socket socket=serverSocket.accept(); // 如果没有客户端连接,会阻塞在这里,直到客户端发送了连接
                log.info("receive connection from client. client:{}",socket.getRemoteSocketAddress());
                byte[] buffer=new byte[64];
                socket.getInputStream().read(buffer); // 如果没有读取到当前客户端发送的数据,会阻塞在这里,直到客户端发送了数据
                log.info("receive message from client. client:{} message:{}",socket.getRemoteSocketAddress(),new String(buffer,"UTF-8"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            serverSocket.close();
        }
    }
}

客户端:

代码语言:javascript
复制
@Slf4j
public class BIOClient {
    @SneakyThrows
    public static void main(String[] args) {
        Socket socket = new Socket();
        try {
            socket.connect(new InetSocketAddress("127.0.0.1", 8080));
            log.info("client connect finished");
            socket.getOutputStream().write("hello".getBytes(StandardCharsets.UTF_8));
            log.info("client send finished");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            socket.close();
        }
    }
}
  • 以Run模式启动服务端
  • 在客户端的 socket.getOutputStream().write("hello".getBytes(StandardCharsets.UTF_8)); 处打上断点,以Debug模式运行一个客户端A,执行到断点时,服务端已经接收到客户端A的请求(在控制台打印了 receive connection from client. client:/127.0.0.1:61501
  • 再以Debug模式运行一个客户端B,服务端没反应,因为这时客户端A还没发送数据,所以服务端目前是在 socket.getInputStream().read(buffer); 的地方阻塞了(还在等着接收客户端A发送数据)
  • 再以Debug模式运行一个客户端C,服务端同样没反应
  • 让客户端A继续运行完,发现服务端读取到客户端A的数据(打印了receive message from client. client:/127.0.0.1:61501 message:hello )后,才能接收到客户端B的连接(打印了receive connection from client. client:/127.0.0.1:61697
  • 让客户端B继续运行完,发现服务端读取到客户端B的数据(打印了receive message from client. client:/127.0.0.1:61697 message:hello )后,才能接收到客户端C的连接(打印了receive connection from client. client:/127.0.0.1:61701

BIO的“阻塞”就体现在这里,当一个服务端线程正在处理或者等待处理某个客户端的请求,是无法为其他客户端服务的。

BIO多线程处理请求

假如当某个客户端连接上服务端后,不写数据或写数据比较慢,其他客户端的请求就不能被及时处理,这时可以通过多线程的方式来解决,每当收到一个新的客户端时,就单独让一个线程专门处理这个客户端的请求,如下:

代码语言:javascript
复制
@Slf4j
public class BIOMultipleServer {
    private final static ExecutorService threadPool= Executors.newCachedThreadPool();
    @SneakyThrows
    public static void main(String[] args) {
        ServerSocket serverSocket=new ServerSocket();
        try {
            serverSocket.bind(new InetSocketAddress("127.0.0.1",8080),50);
            log.info("server started.");
            while (true){
                Socket socket=serverSocket.accept();
                log.info("receive connection from client. client:{}",socket.getRemoteSocketAddress());
                threadPool.submit(new Runnable() {
                    @SneakyThrows
                    @Override
                    public void run() {
                        byte[] buffer=new byte[64];
                        socket.getInputStream().read(buffer);
                        log.info("receive message from client. client:{} message:{}",socket.getRemoteSocketAddress(),new String(buffer,"UTF-8"));
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            serverSocket.close();
        }
    }
}
  • 以Run模式启动服务端
  • 在客户端的 socket.getOutputStream().write("hello".getBytes(StandardCharsets.UTF_8)); 处打上断点,以Debug模式运行一个客户端A,执行到断点时,服务端已经接收到客户端A的请求(在控制台打印了 receive connection from client. client:/127.0.0.1:61517
  • 再以Debug模式运行一个客户端B,执行到断点时,服务端已经接收到客户端B的请求(在控制台打印了 receive connection from client. client:/127.0.0.1:61521
  • 再以Debug模式运行一个客户端C,执行到断点时,服务端已经接收到客户端C的请求(在控制台打印了 receive connection from client. client:/127.0.0.1:61525
  • 让客户端A、B、C继续运行完,服务端也能正常接收消息

这样就可以通过多线程的方式解决服务端不能同时为多个客户端服务的弊端。但又带来了新的问题:每接收一个客户端就用一个线程去处理,如果创建的线程过多,会消耗大量的服务器资源,即使用线程池的方式来限制线程数量和上下文切换,如果多个客户端连接成功后都等待,也会导致服务端的线程都阻塞,治标不治本。因此BIO只适合连接数较少的场景,当连接数较多时,Java NIO的优势就体现出来了。


转载请注明出处——胡玉洋 《Java网络编程——BIO阻塞IO》

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-08-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档