网络基础
传输控制协议TCP简介
- 面向连接的、可靠的、基于字节流的传输层通信协议
- 将应用层的数据流分割成报文段并发送给目标节点的TCP层
- 数据包都有序号,对方收到则发送ACK确认,未收到则重传
- 使用校验和来检验数据在传输过程中是否有误
TCP Flags
- URG:紧急指针标志
- ACK:确认序号标志 应答
- PSH:push标志 数据包立即发送
- RST:重置连接标志 复位;中断一个连接
- SYN:同步序号,用于建立连接过程
- FIN:finish标志,用于释放连接
- 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
- 第一次握手:建立连接时,客户端发送SYN包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
- 第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1],此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
为什么需要三次握手才能建立起连接
- 为了初始化Sequence Number的初始值
- 防止乱码
首次握手的隐患—SYN超时
- Server收到Client的SYN,回复SYN-ACK的时候未收到ACK确认
- Server不断重试直至超时,Linux默认等待63秒才断开连接
- 针对SYN Flood的防护措施
- SYN队列满后,通过tcp_syncookies参数回发SYN Cookie
- 若为正常连接则Client会回发SYN Cookie,直接建立连接
- 保活机制
- 向对方发送保活探测报文,如果未收到响应则继续发送
- 尝试次数达到保活探测数仍未收到响应则中断连接
TCP四次挥手
- TCP采用四次挥手来释放连接
- 第一次挥手: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状态,完成四次挥手。
为什么会有TIME_WAIT状态
- 确保有足够的时间让对方收到ACK包
- 避免新旧连接混淆
为什么需要四次握手才能断开连接
- 因为全双工,发送方和接收方都需要FIN报文和ACK报文
对方关闭socket连接,我方忙于读或写,没有及时关闭连接
- 检查代码,特别是释放资源的代码
- 检查配置,特别是处理请求的线程配置
UDP
- 面向非连接
- 不维护连接状态,支持同时向多个客户端传输相同的消息
- 数据包报头只有8个字节,额外开销较小
- 吞吐量只受限于数据生成速率、传输速率以及机器性能
- 尽最大努力交付,不保证可靠交付,不需要维持复杂的链接状态表
TCP的滑动窗口
- RTT:发送一个数据包到收到对应的ACK,所花费的时间
- RTO:重传时间间隔
- TCP使用滑动窗口做流量控制与乱序重排
- 保证TCP的可靠性
- 保证TCP的流控特性
- AdvertisedWindow=MaxRcvBuffer-(LastByteRcvd-LastByteRead)
- EffectiveWindow=AdvertisedWindow-(LastByteSent - LastByteAcked)
发送方
接收方
HTTPS
- SSL(Security Sockets Layer,安全套接层)
- 为网络通信提供安全及数据完整性的一种安全协议
- 是操作系统对外的API,SSL3.0后更名为TLS
- 采用身份验证和数据加密保证网络通信的安全和数据的完整性
加密方式
- 对称加密:加密和解密都使用同一个密钥
- 非对称加密:加密使用的密钥和解密使用的密钥是不相同的
- 哈希算法:将任意长度的信息转换为固定长度的值,算法不可逆
- 数字签名:证明某个消息或者文件是某人发出/认同的
HTTP
- 请求/响应
- 客户端连接到Web服务器
- 发送HTTP请求
- 服务器接受请求并返回HTTP响应
- 释放连接TCP连接
- 客户端浏览器解析HTML内容
- 回车经历
- DNS解析
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
- 五种可能的取值
- 1xx:指示信息–表示请求已接收,继续处理
- 2xx:成功–表示请求已被成功接收、理解、接受
- 3xx:重定向–要完成请求必须进行更进一步的操作
- 4xx:客户端错误–请求有语法错误或请求无法实现
- 5xx:服务器端错误–服务器未能实现合法的请求
GET和POST
- Http报文层面:GET将请求信息放在URL,POST放在报文体中
- 数据库层面:GET符合幂等性和安全性,POST不符合
- 其他层面:GET可以被缓存、被存储,而POST不行
Cookie和Session
- Cookie
- 是由服务器发给客户端的特殊信息,以文本的形式存放在客户端
- 客户端再次请求的时候,会把Cookie回发
- 服务器接收到后,会解析Cookie生成与客户端相对应的内容
- Session
- 服务器端的机制,在服务器上保存的信息
- 解析客户端请求并操作session id,按需保存状态信息
- 使用Cookie来实现
- 使用URL回写来实现
- Cookie数据存放在客户的浏览器上,Session数据放在服务器上
- Session相对于Cookie更安全
- 若考虑减轻服务器负担,应当使用Cookie
HTTPS数据传输流程
- 浏览器将支持的加密算法信息发送给服务器
- 服务器选择一套浏览器支持的加密算法,以证书的形式回发浏览器
- 浏览器验证证书合法性,并结合证书公钥加密信息发送给服务器
- 服务器使用私钥解密信息,验证哈希,加密响应消息回发浏览器
- 浏览器将支持的加密算法信息发送给服务器
- 服务器选择一套浏览器支持的加密算法,以证书的形式回发浏览器
- 浏览器验证证书合法性,并结合证书公钥加密信息发送给服务器
- 服务器使用私钥解密信息,验证哈希,加密响应消息回发浏览器
- 浏览器解密响应消息,并对消息进行验真,之后进行加密交互数据
- HTTPS需要到CA申请证书,HTTP不需要
- HTTPS密文传输,HTTP明文传输
- 连接方式不同,HTTPS默认使用443端口,HTTP使用80端口
- HTTPS=HTTP+加密+认证+完整性保护,较HTTP安全
- 浏览器默认填充http://,请求需要进行跳转,有被劫持的风险
- 可以使用HSTS(HTTP Strict Transport Security)优化
Linux
- 体系结构主要分为用户态(用户上层活动)和内核态
- 内核:本质是一段管理计算机硬件设备的程序
- 系统调用:内核的访问接口,是一种能再简化的操作
- 公用函数库
uname -a
man 2 syscalls 所有系统调用
man 2 acct 系统调用说明
使用管道注意的要点
- 只处理前一个命令正确输出,不处理错误输出
- 右边命令必须能够接收标准输入流,否则传递过程中数据会被抛弃
- sed,awk,grep,cut,head,top,less,more,wc.join,sort,split等
awk
- 一次读取一行文本,按输入分隔符进行切片,切成多个组成部分
- 将切片直接保存在内建的变量中,$1,$2…($0表示行的全部)
- 支持对单个切片的判断,支持循环判断,默认分隔符为空格
awk '{print $1,$4}' netstat.txt
awk '$1=="tcp"&&$2==1{print $0}' netstat.txt
awk '{enginearr[$1]++}END{for(i in enginearr)printi"\t"enginearr[i]}'
sed
- 全名stream editor,流编辑器
- 适合用于对文本的行内容进行处理
sed -i 's/^Str/String/' replace.java
sed -i 's\.$/\;/' replace,java
sed -i 's/Jack/me/g' replace java
Java
Compile Once,Run Anywhere
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。
- javap -c sourcejavafile
- 虚指令
- 为什么JVM不直接将源码解析成机器码去执行
- 准备工作:每次执行都需要各种检查
- 兼容性:也可以将别的语言解析成字节码
JVM如何加载.class文件
- Class Loader:依据特定格式,加载class文件到内存
- Execution Engine:对命令进行解析
- Native Interface:融合不同开发语言的原生库为Java所用
- Runtime Data Area:JVM内存空间结构模型
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectSample {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
Class rc = Class.forName("com.interview.javabasic.reflect.Robot");
Robot r = (Robot) rc.newInstance();
System.out.println("Class name is " + rc.getName());
Method getHello = rc.getDeclaredMethod("throwHello", String.class);
getHello.setAccessible(true);
Object str = getHello.invoke(r, "Bob");
System.out.println("getHello result is " + str);
Method sayHi = rc.getMethod("sayHi", String.class);
sayHi.invoke(r, "Welcome");
Field name = rc.getDeclaredField("name");
name.setAccessible(true);
name.set(r, "Alice");
sayHi.invoke(r, "Welcome");
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));
}
}
类从编译到执行的过程
ClassLoader在Java中有着非常重要的作用,它主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader 负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。
- 编译器将Robotjava源文件编译为Robot.class字节码文件
- ClassLoader将字节码转换为JVM中的
Class<Robot>
对象 - JVM利用Class对象实例化为Robot对象
- ClassLoader种类
- BootStrapClassLoader:C++编写,加载核心库 java.*
- ExtClassLoader:Java编写,加载扩展库 javax.*
- AppClassLoader:Java编写,加载程序所在目录
- 自定义ClassLoader:Java编写,定制化加载
protected Class<?>findClass(String name)throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
protected final Class<?> defineClass(byte[] b,int off,int len)
throws ClassFormatError
{
return defineClass(name:null,b,off,1en,protectionDomain:null);
}
自定义ClassLoader
双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。
public class MyClassLoader extends ClassLoader {
private String path;
private String classLoaderName;
public MyClassLoader(String path, String classLoaderName) {
this.path = path;
this.classLoaderName = classLoaderName;
}
//用于寻找类文件
@Override
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
//用于加载类文件
private byte[] loadClassData(String name) {
name = path + name + ".class";
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(new File(name));
out = new ByteArrayOutputStream();
int i = 0;
while ((i = in.read()) != -1) {
out.write(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return out.toByteArray();
}
}
类的加载方式
隐式加载:new
显式加载:loadClass,forName等
- 类装载过程
- 加载
- 通过ClassLoader加载class文件字节码,生成Class对象
- 链接
- 校验:检查加载的class的正确性和安全性
- 准备:为类变量分配存储空间并设置类变量初始值
- 解析:JVM将常量池内的符号引用转换为直接引用
- 初始化
- 执行类变量赋值和静态代码块
- 加载
- Class.forName得到的class是已经初始化完成的
- Classloder.loadClass得到的class是还没有链接的
内存模型
- 32位处理器:232的可寻址范围
- 64位处理器:264的可寻址范围
地址空间的划分
- 内核空间
- 用户空间
- 线程私有:程序计数器、虚拟机栈、本地方法栈
- 线程共享:MetaSpace、Java堆
- 程序计数器(Program Counter Register)
- 当前线程所执行的字节码行号指示器(逻辑)
- 改变计数器的值来选取下一条需要执行的字节码指令
- 和线程是一对一的关系即“线程私有"
- 对Java方法计数,如果是Native方法则计数器值为Undefined
- 不会发生内存泄露
- Java虚拟机栈(Stack)
- Java方法执行的内存模型
- 包含多个栈帧
- 局部变量表:包含方法执行过程中的所有变量
- 操作数栈:入栈、出栈、复制、交换、产生消费变量
- 递归为什么会引发 java.lang.StackOverflowError异常
- 递归过深,栈帧数超出虚拟栈深度
- 虚拟机栈过多会引发java.lang.OutOfMemoryError异常
- 本地方法栈
- 与虚拟机栈相似,主要作用于标注了native的方法
JVM三大性能调优参数-Xms-Xmx-Xss的含义
java -Xms128m -Xm×128m -Xss256k -jar xxxx.jar
- Xss:规定了每个线程虚拟机栈(堆栈)的大小
- Xms:堆的初始值
- Xmx:堆能达到的最大值
Java内存模型中堆和栈的区别一内存分配策略
- 静态存储:编译时确定每个数据目标在运行时的存储空间需求
- 栈式存储:数据区需求在编译时未知,运行时模块入口前确定
- 堆式存储:编译时或运行时模块入口都无法确定,动态分配
- 引用对象、数组时,栈里定义变量保存堆中目标的首地址
Java内存模型中堆和栈的区别
- 管理方式:栈自动释放,堆需要GC
- 空间大小:栈比堆小
- 碎片相关:栈产生的碎片远小于堆
- 分配方式:栈支持静态和动态分配,而堆仅支持动态分配
- 效率:栈的效率比堆高
不同JDK版本之间的intern()方法的区别一 JDK6 JDK6+
String s = new String("a");
s.intern();
JDK6:当调用intern方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串对象的引用。
JDK6+:当调用intern方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,如果该字符串对象已经存在于Java堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。
共同学习,写下你的评论
评论加载中...
作者其他优质文章