课前回顾:
1.序列化流(读写对象)
2.注意:如果想实现序列化,那么被序列化的类要实现Serializable接口
3.序列化流:ObjectOutputStream
writeObject(Object o)
反序列化流:ObjectInputStream
Object readObject()
4.注意:
a.为防止序列号冲突,我们将序列号定死:定义static final long
b.不能随便循环反序列化:EOFException
循环次数必须和存储对象的个数一样->将每个对象存到集合中
5.Properties
load(InputStream in)->将流中的数据加载到properties集合中
6.打印流:PrintStream
println:原样输出,自带换行
print:原样输出,不带换行
改变流向:System.setOut(打印流对象)
7.Lombok插件:@Data
8.正则表达式:有特殊规则的字符串
String中有一个方法,验证字符串是否符合正则表达式:boolean matches(正则表达式)
[0-9]: 表示0-9任意一个数字
[abc]: 表示a或者b或者c
\\d:相当于[0-9]
.:匹配任意字符
x+:x至少出现1次
x*:x出现任意次
x{n}:x出现必须是n次
x{n,}:x至少出现n次
x{n,m}:x出现n次到m次
9.单例模式:让一个类只产生一个对象
饿汉式:让对象马上产生,所以定义为private static的
懒汉式:什么时候用,什么时候new ->需要双重检测锁
10.Commons-io:简化IO开发的工具类
IOUtils:
IOUtils.copy(InputStream in,OutputStream out)
IOUtils.closeQuietly(任意流对象)悄悄的释放资源
FileUtils:copyDirectoryToDirectory(File src,File dest)
writeStringToFile(File file,String str)
String readFileToString(File file)
今日内容:
1.会简单定义枚举,会调用枚举中的成员
2.了解软件架构
3.知道网络通信三要素
4.知道完成通信之前的三次握手
5.知道客户端和服务端完成交互的过程
6.会使用TCP完成简单的客户端和服务端交互
一.枚举
1.枚举介绍
1.当[对象]的个数是有限的,确定的,我们就可以定义为一个枚举类
当定义一组常量时,推荐使用枚举
枚举:
1.概述:引用数据类型
类 数组 接口 注解 [枚举]
2.关键字:enum->java.lang.Enum类,是所有枚举的父类。
3.枚举:成员很单一,里面一般都定义常量(默认的修饰符 public static final但是定义的时候写上报错)
4.特点:定义的常量都是这个枚举类的对象
一个枚举类中有多个对象(多例)
5.问题:我们定义出来的常量,数据类型应该是什么:本类类型
6.使用场景:一般用在表示状态上(如果用1234表示状态不太好,用枚举是最好的)
提交订单: 未付款
提交订单: 已付款
提交订单: 未发货
提交订单: 已发货(发货中)
提交订单: 派件中
提交订单: 已签收
7.枚举中定义构造:(了解)
a.无参构造:默认权限修饰符是private
b.有参构造:private,要是赋值的话,我们可以利用常量直接赋值 :RED("红色")
/*
Color叫 枚举类
枚举类中的常量叫枚举
*/
public enum Color {
RED,//Color RED = new Color()
GREEN,//Color GREEN = new Color()
YELLOW;//Color YELLOW = new Color()
}
public class Test01_Color {
public static void main(String[] args) {
Color red = Color.RED;
//System.out.println(red);
System.out.println(red.toString());
}
}
public enum State {
WEIFUKUAN("未付款"),//State WEIFUKUAN = new State() State WEIFUKUAN = new State("未付款")
YIFUKUAN("已付款"),//State YIFUKUAN = new State() State WEIFUKUAN = new State("已付款")
WEIFAHUO("未发货"),//State WEIFAHUO = new State() State WEIFAHUO = new State("未发货")
YIFAHUO("已发货"),//State YIFAHUO = new State() State YIFAHUO = new State("已发货")
YIQIANSHOU("已签收");//State YIQIANSHOU = new State() State YIQIANSHOU = new State("已签收")
private State(){
}
//定义成员变量
private String state;
private State(String state) {
this.state = state;
}
//提供一个get方法获取枚举值
public String getState() {
return state;
}
}
public class Test02_State {
public static void main(String[] args) {
State weifukuan = State.WEIFUKUAN;
System.out.println(weifukuan.getState());
}
}
2.枚举的方法_Enum
方法名 | 说明 |
---|---|
String toString() | 返回枚举常量的名称,它包含在声明中 |
values() | 返回枚举类型的对象数组,可以快速遍历出所有的枚举值 |
valueOf(String str) | 将一个字符串转成枚举类型 |
public enum Color {
RED("红色"),//Color RED = new Color()
GREEN("绿色"),//Color GREEN = new Color()
YELLOW("黄色");//Color YELLOW = new Color()
private String color;
Color() {
}
Color(String color) {
this.color = color;
}
public String getColor() {
return color;
}
}
public class Test03_Method {
public static void main(String[] args) {
//String toString()返回枚举常量的名称,它包含在声明中
Color red = Color.RED;
System.out.println(red.toString());
System.out.println("=======================");
//values()返回枚举类型的对象数组,可以快速遍历出所有的枚举值
Color[] values = Color.values();
for (Color value : values) {
System.out.println(value.getColor());
}
System.out.println("=====================");
// valueOf(String str) 将一个字符串转成枚举类型
Color red1 = Color.valueOf("RED");
System.out.println(red1);
}
}
小结:
1.枚举主要是做状态使用
2.枚举类中的常量(枚举),都是当前枚举类的对象
3.如果想给枚举赋值,需要提供有参构造
4.使用:类名直接调用
5.注意:
枚举类中的枚举:被public static final修饰
枚举类中的构造:必须是private的
二. 软件结构
- C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、红蜘蛛、飞秋等软件。
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有IE、谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。
三.服务器概念
1.安装了服务器软件的计算接,才能称之为是一台服务器
网络通信协议:两台计算机在做数据交互时要遵守的规则,协议会对数据的格式,速率等进行规定,只有都遵守了这个协议,才能完成数据交互
两台计算机想完成数据交互,需要遵守网络通信协议
三.通信三要素
[IP地址]:计算机的唯一标识,用于两台计算机之间的连接
a.概述:指互联网协议地址(Internet Protocol Address),俗称IP
计算机的唯一标识
b.作用:可用于计算机和计算机之间的连接
c.IPV4
32位的二进制数,通常被分为4个字节,表示成a.b.c.d 的形式,例如192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。
IPV6
为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789->号称能给地球上的每一粒沙子分配一个IP地址
d.查看ip的命令:ipconfig
测试是否能连接其他计算机的命令:ping ip地址
e:特殊的网址:代表的是本机地址,到了哪里都不会变,代表自己
127.0.0.1
localhost
[协议]
TCP:面向连接协议
需要先确认连接,才能进行数据交互
三次握手:
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
好处:数据安全,能给数据的传输提供一个安全的传输环境
坏处:效率低
UDP:面向无连接协议
好处:效率高
坏处:传输的数据不安全,容易丢失数据包
[端口号]
每一个应用程序的唯一标识
用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
TCP协议中的三次握手和四次挥手
三次握手:
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
四次挥手:
- 第一次挥手:客户端向服务器端提出结束连接,让服务器做最后的准备工作。此时,客户端处于半关闭状态,即表示不再向服务器发送数据了,但是还可以接受数据。
- 第二次挥手:服务器接收到客户端释放连接的请求后,会将最后的数据发给客户端。并告知上层的应用进程不再接收数据。
- 第三次挥手:服务器发送完数据后,会给客户端发送一个释放连接的报文。那么客户端接收后就知道可以正式释放连接了。
- 第四次挥手:客户端接收到服务器最后的释放连接报文后,要回复一个彻底断开的报文。这样服务器收到后才会彻底释放连接。这里客户端,发送完最后的报文后,会等待2MSL,因为有可能服务器没有收到最后的报文,那么服务器迟迟没收到,就会再次给客户端发送释放连接的报文,此时客户端在等待时间范围内接收到,会重新发送最后的报文,并重新计时。如果等待2MSL后,没有收到,那么彻底断开。
小结:
1.服务器:安装了服务器软件的计算机
2.通信三要素:
IP:每一台计算机的唯一标识,连接计算机的
协议:TCP(重点)面向连接协议(三次握手,四次挥手) UDP(面向无连接协议)
端口号:每一个应用程序的唯一表示
3.软件架构:
CS:安装app-> lol QQ 迅雷等
BS:可以通过浏览器访问-> 淘宝网站 学生信息网等
四.实现简单客户端和服务端的交互
一.编写客户端
1.创建Socket对象,指明服务端的IP以及端口号
2.调用Socket中的getOutputStream,往服务端发请求(写数据)
3.调用Socket中的getInputStream,读取服务端发来的响应结果(读数据)
4.关闭资源
public class Client {
public static void main(String[] args)throws Exception {
//1.创建Socket对象,指明服务端的IP以及端口号
Socket socket = new Socket("127.0.0.1", 6666);
//2.调用Socket中的getOutputStream,往服务端发请求(写数据)
OutputStream os = socket.getOutputStream();
os.write("你好吗?服务器".getBytes());
//3.调用Socket中的getInputStream,读取服务端发来的响应结果(读数据)
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int read = is.read(bytes);
System.out.println(new String(bytes,0,read));
//4.关闭资源
is.close();
os.close();
socket.close();
}
}
二.编写服务端
1.创建ServerSocket对象,指明端口号
2.调用ServerSocket中的accept方法,等待连接服务器的客户端对象
3.调用Socket中的getInputStream,读取客户端发来的请求(读数据)
4.调用Socket中的getOutputStream,往客户端写响应(写数据)
5.关闭资源
public class Server {
public static void main(String[] args)throws Exception {
//1.创建ServerSocket对象,指明端口号
ServerSocket ss = new ServerSocket(6666);
//2.调用ServerSocket中的accept方法,等待连接服务器的客户端对象
Socket socket = ss.accept();
//3.调用Socket中的getInputStream,读取客户端发来的请求(读数据)
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int read = is.read(bytes);
System.out.println(new String(bytes,0,read));
//4.调用Socket中的getOutputStream,往客户端写响应(写数据)
OutputStream os = socket.getOutputStream();
os.write("我不好,客户端".getBytes());
//5.关闭资源
os.close();
is.close();
socket.close();
ss.close();
}
}
经验值传授:
Exception in thread “main” java.net.BindException: Address already in use: JVM_Bind
解释:
端口号被占用
原因:连续开启了多次服务器
五.文件上传
一.文件上传客户端以及服务端实现
//客户端
public class Client {
public static void main(String[] args)throws Exception {
//1.创建Socket对象
Socket socket = new Socket("127.0.0.1", 6666);
//2.创建FileInputStream将本地上要上传的图片读到内存中
FileInputStream fis = new FileInputStream("E:\\io\\1.jpg");
//3.调用Socket中的getOutputStream用于将读取到的图片写到服务端
OutputStream os = socket.getOutputStream();
//4.边读编写
byte[] bytes1 = new byte[1024];
int len;
while((len = fis.read(bytes1))!=-1){
os.write(bytes1,0,len);
}
socket.shutdownOutput();
System.out.println("============以下代码是读取服务端发来的响应==========");
//5.调用Socket中的getInputStream,读取服务端发来的数据
InputStream is = socket.getInputStream();
byte[] bytes2 = new byte[1024];
int len1 = is.read(bytes2);
System.out.println(new String(bytes2,0,len1));
//6.关闭资源
is.close();
os.close();
fis.close();
socket.close();
}
}
//服务端
public class Server {
public static void main(String[] args) throws Exception {
//1.创建ServerSocket对象
ServerSocket ss = new ServerSocket(6666);
//2.调用ServerSocket中的accept方法等待获取连接的客户端对象
Socket socket = ss.accept();
//3.调用socket中的getInputStream读取客户端发来的图片数据
InputStream is = socket.getInputStream();
//4.创建FileOutputStream,将读到的图片写到服务端指定的位置
String name = System.currentTimeMillis()+""+new Random().nextInt()+".jpg";
FileOutputStream fos = new FileOutputStream("E:\\io\\upload\\"+name);
//5.边读边写
byte[] bytes1 = new byte[1024];
int len;
while((len = is.read(bytes1))!=-1){
fos.write(bytes1,0,len);
}
System.out.println("======以下代码是给客户端写响应结果========");
//6.调用Socket中的getOutputStream,给客户端写内容
OutputStream os = socket.getOutputStream();
os.write("上传成功".getBytes());
//7.关闭资源
os.close();
fos.close();
is.close();
socket.close();
}
}
二.文件上传服务端实现(多线程)
//服务端
public class Server_Thread {
public static void main(String[] args) throws Exception {
//1.创建ServerSocket对象
ServerSocket ss = new ServerSocket(6666);
while(true){
//2.调用ServerSocket中的accept方法等待获取连接的客户端对象
Socket socket = ss.accept();
new Thread(new Runnable() {
@Override
public void run() {
InputStream is = null;
FileOutputStream fos = null;
OutputStream os = null;
try{
//3.调用socket中的getInputStream读取客户端发来的图片数据
is = socket.getInputStream();
//4.创建FileOutputStream,将读到的图片写到服务端指定的位置
String name = System.currentTimeMillis()+""+new Random().nextInt()+".jpg";
fos = new FileOutputStream("E:\\io\\upload\\"+name);
//5.边读边写
byte[] bytes1 = new byte[1024];
int len;
while((len = is.read(bytes1))!=-1){
fos.write(bytes1,0,len);
}
System.out.println("======以下代码是给客户端写响应结果========");
//6.调用Socket中的getOutputStream,给客户端写内容
os = socket.getOutputStream();
os.write("上传成功".getBytes());
}catch (Exception e){
e.printStackTrace();
}finally {
//7.关闭资源
CloseUtils.close(os,fos,is,socket);
}
}
}).start();
}
}
}
三.文件上传服务器端(连接池版本)
//服务端
public class Server_Thread_Pool {
public static void main(String[] args) throws Exception {
//1.创建ServerSocket对象
ServerSocket ss = new ServerSocket(6666);
//创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(50);
while(true){
//2.调用ServerSocket中的accept方法等待获取连接的客户端对象
Socket socket = ss.accept();
es.submit(new Runnable() {
@Override
public void run() {
InputStream is = null;
FileOutputStream fos = null;
OutputStream os = null;
try{
//3.调用socket中的getInputStream读取客户端发来的图片数据
is = socket.getInputStream();
//4.创建FileOutputStream,将读到的图片写到服务端指定的位置
String name = System.currentTimeMillis()+""+new Random().nextInt()+".jpg";
fos = new FileOutputStream("E:\\io\\upload\\"+name);
//5.边读边写
byte[] bytes1 = new byte[1024];
int len;
while((len = is.read(bytes1))!=-1){
fos.write(bytes1,0,len);
}
System.out.println("======以下代码是给客户端写响应结果========");
//6.调用Socket中的getOutputStream,给客户端写内容
os = socket.getOutputStream();
os.write("上传成功".getBytes());
}catch (Exception e){
e.printStackTrace();
}finally {
//7.关闭资源
CloseUtils.close(os,fos,is,socket);
}
}
});
}
}
}
四.关闭资源工具类
public class CloseUtils {
public static void close(OutputStream os, FileOutputStream fos, InputStream is, Socket socket){
if (os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
六.BS架构服务器案例
一.案例分析
二.BS结构服务器代码实现
/*
如果想调用BufferedReader中的readLine方法,
我们就要想办法将InputStream转成BufferedReader
BufferedReader构造:
BufferedReader(Reader in)
Reader是抽象类,所以需要传递Reader的子类:FileReader InputStreamReader
InputStreamReader的构造:
InputStreamReader(InputStream in)
*/
public class BSServer {
public static void main(String[] args)throws Exception {
//1.创建ServerSocket对象
ServerSocket ss = new ServerSocket(8888);
while(true){
//2.调用accept接收连接的客户端对象
Socket socket = ss.accept();
//3.调用socket中的getInputStream,读取客户端发来的请求
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();// GET /day20/web/index.html HTTP/1.1
String[] s1 = s.split(" ");
String s2 = s1[1];// /day20/web/index.html
String path = s2.substring(1);// day20/web/index.html
//4.创建FileInputStream,根据解析出来的路径,找到本地上的html,读到内存中
FileInputStream fis = new FileInputStream(path);
//5.调用socket中的getOutputStream.将读到的html写会给浏览器
OutputStream os = socket.getOutputStream();
//写响应信息给浏览器
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
//6.边读边写
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
//7.关闭资源
os.close();
fis.close();
br.close();
is.close();
socket.close();
}
}
}
案例总结:
1.创建ServerSocket对象,指定端口号
2.调用accpet()接收连接服务端的客户端对象
3.调用getInputStream,读取请求信息
4.将InputStream转成BufferedReader读取请求信息的第一行(因为第一行是请求路径)
5.解析:将请求路径中的 模块名/文件夹名/index.html 解析出来
6.利用解析出来的路径创建FileInputStream,根据路径读取本地上的index.html
7.调用getOutpuStream,往浏览器上写(响应)index.html
8.在响应之前,将响应给浏览器的响应信息先写给浏览器
9.边读,边写
三.BS结构服务器代码实现(多线程版本)
public class BSServer_Thread {
public static void main(String[] args)throws Exception {
//1.创建ServerSocket对象
ServerSocket ss = new ServerSocket(8888);
while(true){
//2.调用accept接收连接的客户端对象
Socket socket = ss.accept();
new Thread(new Runnable() {
@Override
public void run() {
InputStream is = null;
BufferedReader br = null;
FileInputStream fis = null;
OutputStream os = null;
try{
//3.调用socket中的getInputStream,读取客户端发来的请求
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();// GET /day20/web/index.html HTTP/1.1
String[] s1 = s.split(" ");
String s2 = s1[1];// /day20/web/index.html
String path = s2.substring(1);// day20/web/index.html
//4.创建FileInputStream,根据解析出来的路径,找到本地上的html,读到内存中
fis = new FileInputStream(path);
//5.调用socket中的getOutputStream.将读到的html写会给浏览器
os = socket.getOutputStream();
//写响应信息给浏览器
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
//6.边读边写
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//7.关闭资源
//关流
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
}