目录
3.1 DatagramSocket:发送端和接收端对象(人)
网络通信(Socket)
1 网络通信基本模式
- 常见的通信模式有如下2种形式:Client-Server(CS) 、 Browser/Server(BS)
2 网络通信三要素
- IP地址:设备在网络中的地址,是唯一的标识
- 端口:应用程序在设备中唯一的标识
- 协议: 数据在网络中传输规则,常见的协议有UDP协议和TCP协议
2.1 IP地址
2.1.1 IP地址概述
- IP(Internet Protocol)全称”互联网协议地址”,是分配给上网设备的唯一标志,常见的IP分类为:IPv4和IPv6
- IPv4:32bit(4字节),点分十进制表示法
例如:
11000000 10101000 00000001 01000010 ————> 192.168.1.66
- IPv6:128位(16个字节),号称可以为地球每一粒沙子编号。
IPv6分成8个整数,每个整数用四个十六进制位表示, 数之间用冒号(:)分开,冒分十六进制表示法
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
- IP地址形式:公网地址、和私有地址(局域网使用),192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用
- 本机IP: 127.0.0.1或者localhost:称为回送地址也可称本地回环地址,只会寻找当前所在本机
- IP常用命令:
- ipconfig:查看本机IP地址
- ping IP地址:检查网络是否连通
2.1.2 IP地址操作类-InetAddress
InetAddress API如下:
名称 | 说明 |
public static InetAddress getLocalHost() | 返回本主机的地址对象 |
public static InetAddress getByName(String host) | 得到指定主机的IP地址对象,参数是域名或者IP地址 |
public String getHostName() | 获取此IP地址的主机名 |
public String getHostAddress() | 返回IP地址字符串 |
public boolean isReachable(int timeout) | 在指定毫秒内连通该IP地址对应的主机,连通返回true |
2.2 端口号
- 标识正在计算机设备上运行的进程(程序),被规定为一个 16 位的二进制,范围是 0~65535
- 分类:
- 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
- 注册端口:1024~49151,分配给用户进程或某些应用程序(如:Tomcat占 用8080,MySQL占用3306)
- 动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配
- 一个设备中不能出现两个程序的端口号一样,否则出错
2.3 通信协议
- 连接和通信数据的规则被称为网络通信协议
2.3.1 网络通信协议参考模型
- OSI参考模型:世界互联协议标准,全球通信规范,由于此模型过于理想化,未能在因特网上进行广泛推广(理论)
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准(实际)
2.3.2 TCP协议简述——三次握手建立连接/四次挥手断开连接(重点)
- 使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议
- 传输前,采用“三次握手”方式建立连接,所以是可靠的
- 在连接中可进行大数据量的传输
- 连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低
- TCP三次握手确立连接:
- 客户端向服务器发出连接请求,等待服务器确认
- 服务器向客户端返回一个响应,告诉客户端收到了请求
- 客户端向服务器再次发出确认信息,连接建立
- TCP四次挥手断开连接
- 客户端向服务器发出取消连接请求
- 服务器向客户端返回一个响应,表示收到客户端取消请求
- 服务器将最后的数据处理完毕后,向客户端发出确认取消信息
- 客户端再次发送确认消息,连接取消
2.3.3 UDP协议简述
- UDP是一种无连接、不可靠传输的协议。
- 将数据源IP、目的地IP和端口以及数据封装成数据包,大小限制在64KB内,直接发送出去即可
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的,可以广播发送 ,发送数据结束时无需释放资源,开销小,速度快
3 UDP通信
- UDP是一种无连接、不可靠传输的协议。
- 将数据源IP、目的地IP和端口以及数据封装成数据包,大小限制在64KB内,直接发送出去即可
3.1 DatagramSocket:发送端和接收端对象(人)
- 构造器:
构造器 | 说明 |
public DatagramSocket() | 创建发送端的Socket对象,系统会随机分配一个端口号。 |
public DatagramSocket(int port) | 创建接收端的Socket对象并指定端口号 |
- 方法:
方法 | 说明 |
public void send(DatagramPacket dp) | 发送数据包 |
public void receive(DatagramPacket p) | 接收数据包 |
3.2 DatagramPacket:数据包对象
- 构造器:
构造器 | 说明 |
public DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 创建发送端数据包对象buf:要发送的内容,字节数组length:要发送内容的字节长度address:接收端的IP地址对象port:接收端的端口号 |
public DatagramPacket(byte[] buf, int length) | 创建接收端的数据包对象buf:用来存储接收的内容length:能够接收内容的长度 |
- 常用方法:
方法 | 说明 |
public int getLength() | 获得实际接收到的字节个数 |
3.3 UDP发送接收消息
- 客户端(发送):
- 创建DatagramSocket对象(发送端对象)
- 创建DatagramPacket对象封装需要发送的数据(数据包对象)
- 使用DatagramSocket对象的send方法传入DatagramPacket对象
- 释放资源
- 服务端(接收):
- 创建DatagramSocket对象并指定端口(接收端对象)
- 创建DatagramPacket对象接收数据(数据包对象)
- 使用DatagramSocket对象的receive方法传入DatagramPacket对象
- 释放资源
3.4 UDP多收多发消息
- 多收多发思想:
- 发送端可以一直发送消息。
- 接收端可以不断的接收多个发送端的消息展示。
- 发送端输入了exit则结束发送端程序
- 客户端(反复发送数据):
- 创建DatagramSocket对象(发送端对象)
- 使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
- 如果用户输入的不是exit, 把数据封装成DatagramPacket
- 使用DatagramSocket对象的send方法将数据包对象进行发送
- 释放资源
- 服务端(反复接收数据):
- 创建DatagramSocket对象并指定端口(接收端对象)
- 创建DatagramPacket对象接收数据(数据包对象)
- 使用while死循环不断的进行第4步
- 使用DatagramSocket对象的receive方法传入DatagramPacket对象
3.5 UDP广播、组播(了解)
- 单播:单台主机与单台主机之间的通信
- 广播:当前主机与所在网络中的所有主机通信,广播地址:255.255.255.255
- 组播:当前主机与选定的一组主机的通信,组播地址:224.0.0.0 ~ 239.255.255.255
4 TCP通信
- TCP是一种面向连接,安全、可靠的传输数据的协议
- 传输前,采用“三次握手”方式,点对点通信,是可靠的
- 在连接中可进行大数据量的传输
4.1 Socket/ServerSocket管道类
- 构造器:
构造器 | 说明 |
public Socket(String host , int port) | 创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口。 |
public ServerSocket(int port) | 注册服务端端口 |
- 方法(IO流):
方法 | 说明 |
OutputStream getOutputStream() | 获得字节输出流对象 |
InputStream getInputStream() | 获得字节输入流对象 |
public Socket accept() | 等待接收客户端的Socket通信连接连接成功返回Socket对象与客户端建立端到端通信 |
4.2 TCP发送接收消息
- 客户端(发送):
- 创建客户端的Socket对象,请求与服务端的连接
- 使用socket对象调用getOutputStream()等方法得到字节输出流
- 使用字节输出流完成数据的发送
- 释放资源:关闭socket管道
- 服务端(接收):
- 创建ServerSocket对象,注册服务端端口。
- 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
- 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
- 释放资源:关闭socket管道
4.3 TCP多发多收消息
- 可以使用死循环控制服务端收完消息继续等待接收下一个消息
- 客户端也可以使用死循环等待用户不断输入消息
- 客户端一旦输入了exit,则关闭客户端程序,并释放资源
- 服务端现在只有一个线程,只能与一个客户端进行通信
4.4 TCP同时接受多个客户端消息(重点)
4.4.1 多线程实现接受多个客户端消息
- 主线程定义了循环负责接收客户端Socket管道连接
- 每接收到一个Socket通信管道后分配一个独立的线程负责处理它
4.4.2 使用线程池进行优化
- 服务端可以复用线程处理多个客户端,可以避免系统瘫痪
- 适合客户端通信时长较短的场景
单元测试、反射、注解
1 单元测试
- 单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性
1.1 Junit单元测试框架使用流程
1.2 Junit常用注解
- Junit4
注解 | 说明 |
测试方法 | |
用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 | |
用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 | |
用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 | |
用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
2 反射
- 反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分
- 在运行时,可以直接得到这个类的构造器对象:Constructor
- 在运行时,可以直接得到这个类的成员变量对象:Field
- 在运行时,可以直接得到这个类的成员方法对象:Method
- 需要先得到类对象,然后从类对象中获取类的成分对象
- 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
- 反射的关键:反射的第一步都是先得到编译后的Class类对象(不是“类的对象”),然后就可以得到Class的全部成分
2.1 反射获取类对象
Class c1 = Class.forName(“全类名”);//方式一
Class c2 = 类名.class //方式二
Class c3 = 对象.getClass(); //方式三
2.2 反射获取构造器对象
方法 | 说明 |
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor getConstructor(Class<?>... parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造器对象,存在就能拿到 |
- 获取构造器的作用依然是初始化一个对象返回
符号 | 说明 |
T newInstance(Object... initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
- 注意:如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象,反射可以破坏封装性,私有的也可以执行了
2.3 反射获取成员变量对象
方法 | 说明 |
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
- 获取成员变量的作用依然是在某个对象中取值、赋值
符号 | 说明 |
void set(Object obj, Object value): | 赋值 |
Object get(Object obj) | 获取值。 |
- 如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值
2.4 反射获取方法对象
方法 | 说明 |
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
- 获取成员方法的作用依然是在某个对象中进行执行此方法
符号 | 说明 |
Object invoke(Object obj, Object... args) | 运行方法参数一:用obj对象调用该方法参数二:调用方法的传递的参数(如果没有就不写)返回值:方法的返回值(如果没有就不写) |
- 如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行
2.5 反射的作用
- 绕过编译阶段为集合添加数据:
- 反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的
- 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了
- 作为通用框架的底层原理:
- 给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件/数据库中去
3 注解
3.1 注解的概念
- Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制
- Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注
- 作用:
- 对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定
- 例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行
3.2 自定义注解
public @interface 注解名称 {
public 属性类型 属性名() default 默认值 ;
}
- 特殊属性:如果只有一个属性的情况下,使用该属性的时候可以省略属性名称不写;但是如果有多个属性, 且多个属性没有默认值,那么名称是不能省略的
3.3 元注解
- 元注解就是用于注解注解的注解
- 元注解有两个:
@Target: 约束自定义注解只能在哪些地方使用
@Retention:申明注解的生命周期
3.4 注解解析
- 注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容
- 与注解解析相关的接口:
- Annotation: 注解的祖宗接口,注解都是Annotation类型的对象
- AnnotatedElement:该接口定义了与注解解析相关的解析方法
方法 | 说明 |
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
T getDeclaredAnnotation(Class annotationClass) | 根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
- 所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力
- 解析注解的技巧:
- 注解在哪个成分上,我们就先拿哪个成分对象
- 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
- 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
- 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解