导语
  对于网络编程来说最为典型的就是基于客户端、服务器的C/S模型。也就是说客户端有一个线程,服务器端有一个线程,两个线程之间进行相互的通信。其中服务器段提供的是数据的信息,例如IP端口以及数据等。而客户端进程就是请求获取服务器端的数据,通过TCP的三次握手协议建立连接,连接建立之后通过Socket进行通信。
  在Java的传统操作中ServerSocket作为服务端进行IP和端口的绑定和监听操作;客户端Socket就是发起一次TCP的请求。连接成功之后就采用输入输出流的方式进行同步阻塞式的通信。

下面就来通过简单的BIO通行模型来了解一下BIO的过程。对于BIO来说就是同步阻塞,对于同步来说就是没一个请求都会等到对应的响应完成之后才会继续执行,而对于阻塞来说就是会不会进行等待接收请求而占用资源。这里的资源是指线程资源或者是I/O流。
  首先,服务器端和客户端是一对一的对应关系,也就是说有一个服务器端的请求就有一个客户端的请求,在服务端通过一个独立的Acceptor线程负责监听客户端的请求连接,当它收到客户端的请求之后为每个客户端建立一个新的线程作为处理链路,链路建立之后通过输出流返回应答给客户端,线程销毁。也就是说有多少客户端就建立多少服务器端线程。

  从上面图中可以看到这个模型最大的问题就是弹性伸缩能力。当客户端的并发访问量增加之后,服务端的线程数也会增加,由于在JVM中每个线程都有自己独享的虚拟机栈、本地方法栈、程序计数器等等资源,所以说会导致虚拟机的性能急剧下降。随着并发访问量的增加,JVM就会出现分配内存不够的问题。会发生堆栈溢出、创建新线程失败等问题,并且最终导致宕机或者虚拟机假死,导致服务器不能向外提供服务。
下面通过一个小例子来看一下关于传统BIO编程的操作。
同步阻塞式I/O创建的TimeServer源码分析

public class TimeServer {public static void main(String[] args) throws IOException {int port = 8080;//传入的参数不为空且长度大于零if (args != null && args.length > 0) {port = Integer.valueOf(args[0]);}ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(port);System.out.println("The time server is start in port " + port);Socket socket = null;while (true){socket = serverSocket.accept();new Thread(new TimeServerHandler(socket)).start();}} catch (IOException e) {e.printStackTrace();}finally {if (serverSocket!=null) {serverSocket.close();serverSocket = null;}}}
}

  TimeServer会根据传入的参数来控制监听的端口,如果没有传入对应的参数,默认使用8080端口进行提供服务,会通过一个无限的循环操作来保证服务端始终向外提供服务。如果没有新的客户端接入主线程就会被组塞在accept()操作上。那么首先我们来观察一下服务端启动之后整个虚拟机的变化。

  在这里会发现,主线程被阻塞到了accept方法上,当有新的客户端计入的时候就会有新的变化,这里看一下关于客户端的代码。

public class TimeClinet {public static void main(String[] args) {int port = 8080;if (args!=null&&args.length>0){port = Integer.valueOf(args[0]);}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("QUERY TIME OVER");System.out.println("Send order  2 server succeed.");String resp = in.readLine();System.out.println("Now 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();}}if (socket!=null){try {socket.close();} catch (IOException e) {e.printStackTrace();}socket = null;}}}
}

  客户端通过Socket创建,发送当前查询到了服务器时间,然后读取服务器端的结果并且进行输出,随后关闭对应的连接释放连接资源,客户端进程进行退出。这里提供了一个Handler。

public class TimeServerHandler implements Runnable {private Socket socket;public TimeServerHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {BufferedReader in = null;PrintWriter out = null;try{in = new BufferedReader(new InputStreamReader(socket.getInputStream()));out = new PrintWriter(socket.getOutputStream(),true);String currentTime = null;String body = null;while (true){body = in.readLine();if (body==null){break;}System.out.println("The time server receive order : "+body);currentTime = "QUERY TIME OVER".equalsIgnoreCase(body)? new java.util.Date(System.currentTimeMillis()).toString():"BAD ORDER";out.println(currentTime);}}catch (Exception e){if (in!=null){try {in.close();} catch (IOException e1) {e1.printStackTrace();}}if (out!=null){out.close();out = null;}if (socket!=null){try {socket.close();} catch (IOException e1) {e1.printStackTrace();}socket = null;}}}
}

  通过上面的代码我们可以知道,同步阻塞式的I/O,最大的问题就是在于每个客户端请求服务器都会有一个新的线程区处理。同时一个线程只能接受到一个客户端的连接,这里我们来做一个测试可以让客户端进程多次请求服务器端。我们会发现在在服务器端会出现很多的线程并且导致宕机。那么为什么会出现这种情况,我们知道,如果出现每个客户端线程进行请求操作,对于服务器来说就会创建多个线程进行处理。如果没有一个合理的线程管理机制,任凭线程无限被创建,就会导致服务器的内存溢出,从而创建不了新的线程,无法向外提供服务。

  为了改进这个一对一的线程模型,后来又提出了一个模式,通过线程池的操作来实现一个或者多个线程处理N个客户端的模型,但是尽管实现了这个模式但是它的底层还是BIO的同步阻塞方式。所以被称为是"伪异步"。下一次的博客中就会分享关于伪异步的知识。

总结

  对于BIO来说,理解起来是很简单的,但是真正的使用到实际的场景中,是有一定的难度,在平时做开发的时候并没有注意到这些问题,那是因为在使用的时候并没有像这样详细的分析过,通过上面的一个小例子,可以看出来所有的BIO编程都是离不开这样的一个思路。怎么能很好的设计出高效的代码作为开发是比较重要的能力的培养。

Netty入门笔记-BIO编程相关推荐

  1. [Java入门笔记] 面向对象编程基础(二):方法详解

    2019独角兽企业重金招聘Python工程师标准>>> 什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能 ...

  2. python语言支持函数式编程_Python语言之Pyhton入门笔记函数式编程

    本文主要向大家介绍了Python语言之Pyhton入门笔记函数式编程,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. 一,匿名函数 def add(x,y) return x+y ...

  3. Netty入门之BIO,NIO和AIO编程

    Netty简介 Netty 的介绍 Netty 是由 JBOSS 提供的一个 Java 开源框架,现为 Github 上的独立项目. Netty 是一个异步的.基于事件驱动的网络应用框架,用以快速开发 ...

  4. Netty入门笔记-Linux网络I/O模型介绍

    在之前的博客中并没有将关于Netty的知识系统的总结起来.从这篇博客开始就将关于Netty的有关知识点总结起来顺便提升自己的分析问题的能力,通过博客分享的形式将学习的知识点形成体系,希望也可以帮助大家 ...

  5. Netty入门笔记-I/O多路复用技术

    回顾   上次博客结尾的时候简单提到了多路复用技术.在I/O编程过程中,如果需要多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理.I/O多路复用技术是通过把多个I/O的阻塞复用到同一 ...

  6. Netty(二)(入门篇)传统的Bio编程

    假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情 ...

  7. 《netty入门与实战》笔记-02:服务端启动流程

    为什么80%的码农都做不了架构师?>>>    1.服务端启动流程 这一小节,我们来学习一下如何使用 Netty 来启动一个服务端应用程序,以下是服务端启动的一个非常精简的 Demo ...

  8. ROS入门笔记(十二):动作编程 (C++)

    ROS入门笔记(十二):动作编程 (C++) 文章目录 01 导读 02 功能包的创建 03 在功能包中创建action(动作) 3.1 自定义action 3.2 在package.xml中添加功能 ...

  9. 深度学习入门笔记(五):神经网络的编程基础

    欢迎关注WX公众号:[程序员管小亮] 专栏--深度学习入门笔记 声明 1)该文章整理自网上的大牛和机器学习专家无私奉献的资料,具体引用的资料请看参考文献. 2)本文仅供学术交流,非商用.所以每一部分具 ...

最新文章

  1. CEPH添加MDS操作
  2. 【Python-ML】神经网络-多层感知器
  3. SAP C/4HANA到底包含哪些产品?
  4. properties 配置 java_Java 操作Properties配置文件详解
  5. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1103:陶陶摘苹果
  6. java 的toString()函数
  7. html怎么建立段落,HTML 段落
  8. Android开发之5.0已以上版本沉浸式状态栏
  9. c语言试卷大全,C语言试题大全
  10. 线程池的种类,区别和使用场景
  11. u-boot-2012.04.01移植笔记——支持NAND启动
  12. 32位汇编第三讲,RadAsm,IDE的配置和使用,以及汇编代码注入方式
  13. Mybatis技术的使用一:逆向工程
  14. EXCEL表格-绝对引用符$详解
  15. 网络通信安全基础和OpenSSL
  16. 来自百度,为什么要重构(Refactoring)
  17. matlab里qmul,哈工大四系导航原理 大作业 INS部分
  18. 2022软件测试自学全套教程-基于python自动化软件测试-2022新版软件测试中级程序员学习路线
  19. 东大22春电子政务X《电子政务》在线平时作业3参考非答案
  20. unity3d如何获知场景中需要加载的数据_游戏中遮挡剔除方案总结

热门文章

  1. eclipse java不能编译_eclipse里.java可以编译但不能运行??
  2. java实现二叉查找树_二叉查找树BST----java实现(示例代码)
  3. 第一章 对象引论02
  4. 设置linux的console为串口【转】
  5. 系统补丁自动批量安装
  6. Socket相关操作超时
  7. android启动效果
  8. 淘宝成全球电商第一人气网站
  9. 《并行计算的编程模型》一3.8.3 原子交换和条件交换
  10. 区块链产业生态、存在问题及政策建议|一文读懂新趋势