Netty作为软件高级编程必学技术框架

目录

  • 传统BIO框架
    • 基础回顾-网络七层模型
    • 基础回顾-TCP 报文
    • 题外扩展-TCP三次握手与四次挥手
    • BIO样例
      • 服务端socket
      • 客户端
  • Java里面的IO框架
  • Java里面的线程池
  • 网络IO模型
  • 题外扩展-Java日志框架
  • 题外扩展-https过程说明

传统BIO框架

  1. 网络编程的基本模型是 Client/Server 模型
  2. 两个进程间通信,服务端提供IP和端口,客户端发起连接请求,通过三次握手建立连接
  3. 双方连接建立成功,通过网络套接字(Socket)进行同步阻塞I/O (BIO)通信
  4. 一般情况下服务端的线程数和客户端请求线程数为1 : 1 , 服务端可以创建一个专门线程池防止系统并发量太大而崩溃。

基础回顾-网络七层模型

复习一下,网际互联及OSI七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层,参考博客:https://blog.csdn.net/taotongning/article/details/81352985

每一层都是承上启下的作用,接受上层或下层的数据
发送数据端从应用层 ------》物理层
接受数据端从物理层 ------》应用层

OSI模型 ======= 说明 举例
应用层 为应用软件提供了很多服务 FTP协议,SSH远程登录协议,SMTP/POP3邮件协议,HTTP协议等
表示层 格式化数据 数据的格式化,变成 JPG、GIF格式的数据,数据加密解密等,数据解压缩等
会话层 控制会话,建立管理终止应用程序会话 各类软件中 session的概念
传输层 提供可靠和尽力而为的端到端的数据传输 TCP、UDP协议,3次握手建立连接,负责网络传输和会话建立。
网络层 IP选址和路由选择 链路层 通过MAC地址进行数据通讯,必须同一个局域网,数据完全广播传输。IP地址的出现就是解决不同局域网之间的数据通讯,网关负责转发。
链路层 定义如何格式化二进制数据流,支持错误检测 以太网协议规定一组电信号称之为一个数据包,或者叫做一个“ ”。[head头部 发送接收的MAC地址] + [data数据部分 最短46字节,最长1500字节] 举例:交换机通过MAC地址转发数据,逻辑链路控制
物理层 物理传输、硬件、物理特性 二进制数据流0110…,光纤,网线,无线电波等,该层没有寻址的概念

基础回顾-TCP 报文

TCP报文属于传输层里面的概念,该层的由来:
网络层IP地址帮我们找到目标局域网链路层MAC地址帮我们找到局域网里面的主机传输层端口号帮我们找到具体的应用程序
我们电脑上使用的都是应用程序(微信,QQ,钉钉等),这些应用程序分别占用不同的端口,每一个端口对应一个应用程序。

关于TCP/IP相关知识参考:https://www.coonote.com/tcpip/tcp-ip-basic-knowledge.html

TCP的报文结构

  • 序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
  • 确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=刚接受到的报文里面的Seq + 1, 表示该报文送达接收端时,要求接收端检查 Ack是否为 你之前发的报文里面的Seq + 1

题外扩展-TCP三次握手与四次挥手

复习一下 TCP连接过程,借助网上不错的参考资料:
(1)先看下TCP 6大标志位

您可以看到在3次握手(SYN,ACK)和数据传输期间使用的2个标志。
与所有标志一样,值“1”表示该标志位在传输过程中起作用”
在此示例中,只有“SYN”标志被设置,表示这是新的TCP连接的第一个段。
除此之外,每个标志占一位,由于有6个标志,所以标志部分总共6位:

符号 说明
SYN 同步位,SYN =1表示建立连接请求
ACK 确认位,ACK=1 表示要求接受方确认数据包是否成功接收
FIN 终止位,FIN=1 表示通讯终止请求
RST 重置位,RST=1 表示 TCP 连接中出现异常必须强制断开连接
URG 紧急位,URG=1 表示这是紧急数据
PSH 推送位,PSH=1 表示数据要尽快的交付给应用层

(2)3次握手建立连接

第一次握手
Client发送 SYN=1,随机值seq=J 给Server,
Client进入SYN_SENT状态,等待Server确认

第二次握手
Server收到数据包根据 SYN=1知道Client是要请求建立连接,
Server也发送SYN=1,ACK=1,ack=J+1,随机值 seq=K 给Client用于确认连接请求,
Server进入SYN_RCVD状态

第三次握手
Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则
再次发送ACK=1,ack=K+1给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则
连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,
随后Client与Server之间可以开始传输数据了。

(3)4次挥手端口连接

第一次挥手
Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态

第二次挥手
Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态

第三次挥手
Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态

第四次挥手
Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

如果有大量的连接,每次在连接、关闭时都要三次握手,四次挥手,会很明显会造成性能低下,因此,
HTTP有一种叫做keep connection的机制,它可以在传输数据后仍然保持连接,当客户端再次获取数据时,直接使用刚刚空闲下的连接而无需再次握手。

为什么要三次握手?
1次绝对不行,客户端想连就连,想发数据就发数据那还得了
2次可不可以? 能,但还是有问题,服务端接受到客户端请求就立马在服务端系统中创建连接资源,然后等待客户端应答,客户端的连接有可能是过期的历史请求连接。
4次 多余

  • 三次握⼿才可以阻⽌重复历史连接的初始化(主要原因)(序列号发给客户端,客户端判断过期了直接销毁过时的请求)
  • 三次握⼿才可以同步双⽅的初始序列号
  • 三次握⼿才可以避免资源浪费(就是延迟创建服务端所需资源)

为什么要四次握手?

一般疑问就是 第2、3步骤是否可以合并一步,反正都是服务端发起的?
客户端发起了FIN信号给服务端,如果服务端没有及时回复,在网络世界里面,客户端认为超时了会重试重发第一步的FIN信号。

BIO样例

服务端socket

package org.example.bio;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;/*** bio测试 服务端** @author admin*/
public class BioServer {static Logger logger = LoggerFactory.getLogger(BioServer.class);public static void main(String[] args) throws IOException {// 客户端连接数AtomicLong counter = new AtomicLong(0L);// 服务端端口int serverPort = 9999;// 定义一个专门处理客户端请求的线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 100, 20, TimeUnit.SECONDS,new LinkedBlockingQueue<>(), (ThreadFactory) Thread::new);// 创建服务端 sockettry (ServerSocket server = new ServerSocket(serverPort)) {logger.info("socket编程,服务端启动,端口号: {}", serverPort);while (true) {// 等待一个客户端的请求,线程阻塞方式进行等待Socket socket = server.accept();logger.info("已经累计接受客户端请求数: {} 个", counter.addAndGet(1L));executor.execute(new ClientAcceptHandler(socket));}} catch (Exception e) {logger.error("服务端启动异常", e);}}
}
package org.example.bio;import cn.hutool.core.date.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;/*** bio测试 服务端接受请求后端线程处理** @author admin*/
public class ClientAcceptHandler implements Runnable {static Logger logger = LoggerFactory.getLogger(ClientAcceptHandler.class);private Socket socket;public ClientAcceptHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {String body;while ((body = in.readLine()) != null && body.length() != 0) {logger.info("客户端传递的信息 :{}", body);out.println("来之服务端的响应回复信息: " + DateUtil.now() + "\n");}} catch (Exception e) {logger.error("客户端处理异常", e);} finally {logger.info("客户端处理完毕 socket 被关闭 {}", socket.isClosed());}}
}

客户端

测试时候可以直接 CMD命令行下 通过 CURL命令或者 telnet命令进行测试:

服务端控制台打印信息如下:
一次CURL请求 其实是向服务端发起了4次TCP请求

用java实现一个客户端

package org.example.bio;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;/*** bio测试 客户端** @author admin*/
public class BioClient {static Logger logger = LoggerFactory.getLogger(BioClient.class);public static void main(String[] args) {// 服务端端口int serverPort = 9999;// 服务端地址String serverHost = "localhost";// 创建 sockettry (Socket socket = new Socket(serverHost, serverPort);BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {// 发送信息给服务端// 后面必须带上 \n ,否则会导致服务端in 流读取阻塞out.println("client time " + DateUtil.now() + "\n");// 读取服务端的响应信息String serverbody = in.readLine();if (StrUtil.isNotBlank(serverbody)) {logger.info(serverbody);}} catch (Exception e) {logger.error("客户启动异常", e);}}
}

Java里面的IO框架

网络通信必须掌握的javaIO框架! 上面的例子中包含了输入输出流程常见的操作, 这里记录总结一下 IO框架。

一般按照 操作的数据单元大小分为: 字节流字符流
按照 流的⻆⾊划分为 节点流处理流 下面表格中高 亮部分表示为节点流,所谓 “节点”就是具体的 写入或读取的 “文件” 或 “设备” 或者其他实际对象。

按照流的角色分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串 StringReader StringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流 InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream
抽象基类 FilterInputStream FilterOutputStream FilterReader FilterWriter
打印流 PrintStream PrintWriter
推回输入流 PushbackInputStream PushbackReader
特殊流 DataInputStream DataOutputStream

备注:
System.in作为 InputStream 类的对象实现标准输入,一般用法:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String msg = br.readLine();

System.out 作为 PrintStream 打印流类的的对象实现标准输出,可以调用它的print、println或write方法来输出各种类型的数据。


这里写一个简单的IO测试,文件copy操作


package org.example.io;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.*;/*** @author admin*/
public class StreamTest {static Logger logger = LoggerFactory.getLogger(StreamTest.class);public static void main(String[] args) throws Exception {File file = new File("G:\\tmp.json");File newFile = new File("G:\\newTmp.json");if (newFile.exists() && newFile.delete()) {logger.info("删除文件newTmp.json");}if (newFile.createNewFile()) {logger.info("重新创建文件newTmp.json");}try (InputStream inputStream = new FileInputStream(file);BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));OutputStream outputStream = new FileOutputStream(newFile);BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream))) {String text;while ((text = reader.readLine()) != null) {logger.info("读取tmp.json文本内容: {}", text);bufferedWriter.write(text);bufferedWriter.newLine();logger.info("写入newTmp.json ...");}bufferedWriter.flush();} catch (Exception e) {logger.error("文件操作异常", e);}}
}

处理流 包装了 节点流,操作更加方便,最外面的包装流关闭了被包装的流全部将关闭。

Java里面的线程池

网络编程离不开多线程的探讨,掌握java里面的线程池是必备基础!
参考博客:
https://www.cnblogs.com/zincredible/p/10984459.html
https://www.jianshu.com/p/f030aa5d7a28

java里面的自带的线程池管理类 : ThreadPoolExecutor

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 100, 20, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), (ThreadFactory) Thread::new);// 启动一个线程executor.execute(new MyRunnable());

ThreadPoolExecutor 的常用构造函数参数含义:

参数 说明
int corePoolSize 核心线程大小
int maximumPoolSize 最大线程大小
long keepAliveTime 超过corePoolSize的线程多久不活动被销毁时间
TimeUnit unit 时间单位
BlockingQueue workQueue 任务队列
ThreadFactory threadFactory 线程池工厂
RejectedExecutionHandler handler 拒绝策略

java 内置了常用的四种线程池 ,可由Executors类来生成,它们底层全部由 ThreadPoolExecutor 生成

线程池 说明
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newScheduledThreadPool 创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行
newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

网络IO模型

学习netty之前必须知道的几个IO模型,在弄懂IO模型之前,首先用通俗的示例来理清几个概念 同步 异步 阻塞 非阻塞

业务场景: 你去菜馆点一份菜吃,向菜馆老板报了菜名,老板说OK,马上去厨房做了

各种示例场景 对应的同/异步, 阻/非阻塞
一直站在厨房门口等老板把菜做好,然后你自己把菜端到餐桌上 同步 阻塞
你报完菜名后就去餐桌上刷抖音去了,然后每隔1分钟问老板好了没,如果好了你 自己把菜端到餐桌上 同步非阻塞
你报完菜名后就去餐桌上坐着一动不动,然后菜好了,老板亲自把菜端到餐桌上 异步 阻塞
你报完菜名后就去餐桌上刷抖音去了,然后菜好了,老板亲自把菜端到餐桌上 异步 非阻塞

结论:
你是否亲自去端菜 ? 是=同步 否=异步
你是否可做其他事? 否=阻塞 是=非阻塞

有了上面概念,现在来看看一共五种IO的模型:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO 和 异步IO

推荐博客:https://zhuanlan.zhihu.com/p/115912936
这里截取里面主要图片描述5中IO模型:

非阻塞IO

多路复用IO

信号驱动IO

异步IO

总结:
阻塞非阻塞说的是线程的状态
同步和异步说的是消息的通知机制

同步需要主动读写数据,异步是不需要主动读写数据
同步IO和异步IO是针对用户应用程序和内核的交互

题外扩展-Java日志框架

这里主要是nett测试用例经常用到打印日志,java的日志框架丰富多杂,如何选择一款合适的日志框架?需要了解日志框架的来龙去脉。

不错的微信软文: https://mp.weixin.qq.com/s/v6p6W0MPrtYmVSUwnbW8Dg

总结一下:
日志框架演进历史

日志框架 研发者 说 明
System.out和System.err sun 最早的日志记录方式,不灵活也不可配置
Log4j(Log for Java) 大佬:Ceki Gülcü Apache 建议Sun引入Log4j到java的标准库中,但是sun拒绝了
JUL(Java Util Logging) sun 2002年2月Java1.4发布此日志产品
JCL(Jakarta Commons Logging) Apache 日志接口,提供了一个默认实现Simple Log
Slf4j(Simple Logging Facade forJava) 大佬:Ceki Gülcü 2005年出品,也是一套新日志接口
Logback 大佬:Ceki Gülcü 2006出品, Logback完美实现了Slf4j
Log4j2 Apache 2012年出品, 不是Log4j1.x升级,而是新项目Log4j2,不兼容Log4j1.x的

摘录最后总结图片,三个日志接口之间的相互转换桥接,相爱相杀。

选用指南:

  1. 使用日志接口的API而不是直接使用日志产品的API,一般选择 Slf4j
  2. 自己的项目只选择一款具体日志产品,一般选择 logback 或者 log4j2
  3. 把日志产品的依赖设置为Optional和runtime scope

题外扩展-https过程说明

这里涉及一些域名加密通讯概念,深入了解两点之间的数据传输过程:
https://blog.csdn.net/gzt19881123/article/details/119078215

Netty快速学习1-基础知识回顾相关推荐

  1. 快速学习mysql_快速学习MySQL基础知识

    这篇文章主要梳理了 SQL 的基础用法,会涉及到以下方面内容: SQL大小写的规范 数据库的类型以及适用场景 SELECT 的执行过程 WHERE 使用规范 MySQL 中常见函数 子查询分类 如何选 ...

  2. 【Java基础知识回顾篇】之打怪升级Day001

    Java基础知识回顾篇之打怪升级Day001 目录 Java基础知识回顾篇之打怪升级Day001 简介 一.为什么现在主流的是Java8和Java11? 二.简单尝试编写java程序 1.编写一个He ...

  3. python语言的单行注释以井号开头_推荐|零基础学习Python基础知识

    原标题:推荐|零基础学习Python基础知识 Python是一种面向对象.解释型计算机程序设计语言.语法简洁清晰,强制用空白符作为语句缩进. Python具有丰富和强大的库,又被称为胶水语言.能把其他 ...

  4. Java基础知识回顾之七 ----- 总结篇

    前言 在之前Java基础知识回顾中,我们回顾了基础数据类型.修饰符和String.三大特性.集合.多线程和IO.本篇文章则对之前学过的知识进行总结.除了简单的复习之外,还会增加一些相应的理解. 基础数 ...

  5. 关于图计算图学习的基础知识概览:前置知识点学习(PGL)[系列一]

    关于图计算&图学习的基础知识概览:前置知识点学习(Paddle Graph Learning (PGL)) 0.1图计算基本概念 首先看到百度百科定义: 图计算(Graph Processin ...

  6. html标签怎么快速记忆,seo新手该怎么快速学习html基础代码

    seo新手该怎么快速学习html基础代码 相信站长应该都知道,要做一个网站那么肯定离不开html代码,同样我们做SEO优化肯定也是离不开html代码的优化.那么问题就来了,很多seo新手还不会html ...

  7. Python基础教程(第3版)中文版 第一章 快速上手:基础知识(笔记)

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,分享给大家: https://www.captainai.net/lf 如果你学完了Python不知道干什么,不妨去了解一下. 第一章 快速 ...

  8. 三 计算机知识的重要性分析,学习计算机基础知识对中专学生的重要性分析

    学习计算机基础知识对中专学生的重要性分析 [摘 要]本文主要介绍了计算机基础知识的内容,阐述了学习计算机基础知识对中专学生的作用,并且通过对计算机基础操作的学习,提高中专学生的计算机应用水平.希望本文 ...

  9. 谈计算机知识对学生的作用,浅谈学习计算机基础知识对中专学生的重要性

    [摘 要]本文主要介绍了计算机基础知识的内容,阐述了学习计算机基础知识对中专学生的作用,并且通过对计算机基础操作的学习,提高中专学生的计算机应用水平.希望本文可以让中专学生认识到学习计算机基础知识的重 ...

最新文章

  1. java查询mysql装载bean_jsp与javabean链接mysql数据库并查询数据表的简单实例源码
  2. Pliops XDP(Extreme Data Processor)数据库存储设计的新型加速硬件
  3. DisARM:用于3D目标检测的位移感知关联模块(CVPR2022)
  4. 超维计算让AI有记忆和反应,还能解决自动驾驶难题
  5. 【262】pscp命令 实现windows与linux互传文件
  6. 云主机初体验(盛大云和阿里云)
  7. less webpack 热更新_webpack---less+热更新 使用
  8. Gensee移动SDK之(二)协议
  9. 互联网晚报 | 8月26日 星期四 | 小米Q2营收净利均创单季历史新高;拼多多设立“百亿农研专项”;网易云音乐正式入驻闲鱼...
  10. kibana-7.3.0安装配置
  11. android 携程日历控件,仿携程酒店日历组件for小程序
  12. espresso 2.0.4 Apple Xcode 4.4.1 coteditor 价格
  13. It's not a Bug, it's a Feature! UVA - 658 (最短路)
  14. 2022恒生电子前端笔试
  15. Caffe(12)--实现YOLOv2目标检测
  16. 汇编语言---80386寄存器,GCC内联汇编语法
  17. 如何在 AWS 云中从 Amazon EC2 启动 RHEL 8?
  18. volvo手机能用鸿蒙系统吗,不是华为手机,也能用上鸿蒙系统
  19. Java入门之Digital eigenvalue
  20. C语言学习之——课程大纲

热门文章

  1. oracle19c创建表空间,Oracle19c 创建表空间
  2. 攻防世界各类题目相关
  3. 大数据相关技术入门(基于CentOS7)
  4. docker 问题集
  5. 谈一谈单片机开发的几种调试方案
  6. 【Mac 教程系列第 14 篇】如何设置 Mac 允许从任意来源下载 App
  7. python有哪些细节描写_细节描写训练,莫失良机
  8. ANSYS workbench 根据坐标施加载荷- external data载荷映射
  9. 币圈炒币如何避免被额韭菜?
  10. 通过rustlings源码了解rust如何从命令行参数里面获取值的方式