网络通信概述
- C/S 结构:客户端服务器
- B/S 结构:浏览器服务器
网络编程:在一定的协议下,实现两台计算机的通信的程序
-
网络通信协议:连接和通信的规则
-
TCP/IP协议:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qga79mqh-1570465781187)(en-resource://database/7806:1)] -
协议分类:
- UDP 用户数据报协议。无连接通信协议。
- 耗资小,通信效率高。不能保证数据的完整性
- TCP 传输控制协议。面向连接的通信协议。
- 创建 三次握手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPviEQAA-1570465781189)(en-resource://database/7808:1)] - 断开 四次挥手
- 创建 三次握手
- UDP 用户数据报协议。无连接通信协议。
-
网络编程三要素
- 协议:网络通信遵守的规则
- IP地址:IPv4 42亿个,IPv6
- 端口号
TCP
Client Server
两端通信步骤
1、服务端程序需要实现启动,等待客户端的连接
2、客户端主动连接服务端,连接成功才能通信
服务器端必须明确2件事情
1、多个客户端同时和服务器进行交互,服务器必须明确和哪个客户端进行的交互、在服务器端有一个方法,叫accept,客户端获取到请求的客户端对象
2、多个客户端同时和服务器进行交互,就需要使用多个IO流对象
服务器时没有IO流,服务器可以获取到请求的客户端对象Socket,使用每个客户端Socket中提供的IO流和客户端进行交互
服务器使用客户端的字节输入流读取客户端发送的数据
服务器使用客户端的字节输出流给客户端回写数据
简单记:服务器使用客户端的流和客户端交流
在Java中,2个类用于实现TCP通信
1、客户端:java.net.Socket
2、服务端:java.net.ServerSocket,创建ServerSocket对象,相当于开启一个服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Du2pZwB-1570465781189)(en-resource://database/7928:1)]
Socket
客户端
TCP 通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据,
表示客户端的类:
java.net.Socket:实现客户端套接字。套接字是两台机器之间的断点
套接字:包含了IP地址和端口号的网络单位
构造方法:
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
参数:
String host: 服务器主机的名称/服务器的IP地址
Int port:服务器的端口号
成员方法:
OutputStream getOutputStream() 返回此套接字的输出流
InputSteam getInputSteam() 返回此套接字的输入流
close() 关闭套接字
实现步骤:
1、创建客户端对象 Socket, 构造方法中绑定服务器的IP地址和端口号
2、使用Socket对象中的方法 getOutputStream() 获取网络字节输出流 OutputStream对象
3、使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
4、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
5、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
6、释放资源(Socket)
注意:
1、客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
2、当我们创建客户端对象Socket的时候,就回去请求服务器,和服务器经过3次握手建立连接通路,如果服务器没有启用,
这时如果服务器没有启动,那么就会抛出异常
如果服务器已经启动,那么就可以进行交互了
public class TCPClient {
public static void main(String[] args) throws IOException {
// 1、创建客户端对象 Socket, 构造方法中绑定服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1", 8888);
// 2、使用Socket对象中的方法 getOutputStream() 获取网络字节输出流 OutputStream对象
OutputStream os = socket.getOutputStream();
// 3、使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
os.write("Hello, socket!".getBytes());
// 4、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
// 5、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
// 6、释放资源(Socket)
socket.close();
}
}
服务端
TCP 通信的服务器端:接受客户端的请求,读取客户端发送的数据,给客户端回写数据
表示服务器的类:
java.net.ServerSocket
构造方法:
ServerSocket(int port) 创建绑定到特定端口的服务器套接字
服务器端必须明确一件事情,必须知道的是哪个客户端请求的服务器
所以可以使用accept方法获取请求的客户端对象Socket
成员方法:
Socket accept 侦听并接受此套接字的连接
实现步骤:
1、使用创建服务器ServerSocket 对象和系统要指定的端口号
2、使用ServerSocket对象中的方法accept,获取请求的客户端对象
3、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
4、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
5、使用Socket对象中的方法 getOutputStream() 获取网络字节输出流 OutputStream对象
6、使用网络字节输出流 OutputStream对象的方法write,给客户端回写数据
7、释放资源(Socket,ServerSocket)
public class TCPServer {
public static void main(String[] args) throws IOException {
// 1、使用创建服务器ServerSocket 对象和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
// 2、使用ServerSocket对象中的方法accept,获取请求的客户端对象
Socket socket = server.accept();
// 3、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
InputStream is = socket.getInputStream();
// 4、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes, 0, len));
// 5、使用Socket对象中的方法 getOutputStream() 获取网络字节输出流 OutputStream对象
OutputStream os = socket.getOutputStream();
// 6、使用网络字节输出流 OutputStream对象的方法write,给客户端回写数据
os.write("receive, thanks!".getBytes());
// 7、释放资源(Socket,ServerSocket)
socket.close();
server.close();
}
}
TCP上传案例
原理:客户端读取本地文件,把文件上传到服务器,服务器把上传的文件保存在服务器的硬盘上
步骤:
1、客户端使用 本地的字节输入流,读取要上传的文件
2、客户端使用 网络字节输出流,把读取的文件上传到服务器
3、服务器使用 网络字节输入流,读取客户端上传的文件
4、服务器使用 本地字节输出流,吧读取的到文件,保存在服务器的硬盘上
5、服务器使用 网络字节输出流,给客户端一个“上传成功”
6、客户端使用 网络字节输入流,读取服务端回写的数据
7、释放资源
注意:
客户端和服务器和本地硬盘进行读写,需要使用自己创建的字节流对象(本地流)
客户端和服务端之间进行读写,必须使用Socket中提供的字节流对象(网络流)
- 服务端
文件上传案例的服务器端:读取客户端上传的文件,保存到服务的硬盘,给客户端回写“上传成功”
数据源:客户端上传的文件
目的地:d:\upload\1.gif
实现步骤:
1、使用创建服务器ServerSocket 对象和系统要指定的端口号
2、使用ServerSocket对象中的方法accept,获取请求的客户端对象
3、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
4、判断 目的地是否存在,不存在则创建
5、创建一个本地字节输出流FileOutputStream对象,构造方法中绑定输入的目的地路径
6、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
7、使用本地字节输出流 FileOutputStream中的write方法,把读取的文件保存到服务器的硬盘上
8、使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputSteam对象
9、使用网络字节输出流OutputSteam对象的方法write给客户端回写 “上传成功”
10、释放资源(FileOutputSteam, Socket, ServerSocket)
public class TCPServer {
public static void main(String[] args) throws IOException {
// 1、使用创建服务器ServerSocket 对象和系统要指定的端口号
ServerSocket server = new ServerSocket(8888);
// 2、使用ServerSocket对象中的方法accept,获取请求的客户端对象
/*
让服务器一直处于监听状态(死循环accept方法)
有一个客户端上传文件,就保存一个文件
*/
while (true){
Socket socket = server.accept();
/*
使用多线程技术,提高程序的效率
有一个客户端上传文件,就保存一个文件
*/
new Thread(new Runnable() {
@Override
public void run() {
try {
// 3、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
InputStream is = socket.getInputStream();
// 4、判断 目的地是否存在,不存在则创建
File file = new File("d:\\Data\\upload");
if(!file.exists())
file.mkdirs();
// 5、创建一个本地字节输出流FileOutputStream对象,构造方法中绑定输入的目的地路径
String filename = "paulson" + System.currentTimeMillis() + new Random().nextInt(99999) + ".jpg";
FileOutputStream fos = new FileOutputStream(file + "\\" + filename);
// 6、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
int len = 0;
byte[] bytes = new byte[1024];
while ((len = is.read(bytes))!=-1){
// 7、使用本地字节输出流 FileOutputStream中的write方法,把读取的文件保存到服务器的硬盘上
fos.write(bytes, 0, len);
}
// 8、使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputSteam对象
// 9、使用网络字节输出流OutputSteam对象的方法write给客户端回写 “上传成功”
socket.getOutputStream().write("上传成功".getBytes());
// 10、释放资源(FileOutputSteam, Socket, ServerSocket)
fos.close();
socket.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
// 死循环,不用关闭服务
// server.close();
}
}
- 客户端
文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器返回的数据
数据源:D:\Data\1.gif
目的器:服务器
步骤:
1、创建一个本地的字节输入流 FileInputSteam 对象,构造方法中绑定要读取的数据源
2、创建一个客户端Socket对象,构造方法中绑定服务器的ip地址和端口号
3、使用Socket中的方法getOutputStream对象中的方法read,读取本地文件
4、使用网络字节输入流FileInputStream对象中的read方法,读取本地文件
5、使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
6、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
7、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
8、释放资源(Socket)
public class TCPClient {
public static void main(String[] args) throws IOException {
// 1、创建一个本地的字节输入流 FileInputSteam 对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("D:\\Data\\1.gif");
// 2、创建一个客户端Socket对象,构造方法中绑定服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1", 8888);
// 3、使用Socket中的方法getOutputStream对象中的方法read,读取本地文件
OutputStream os = socket.getOutputStream();
// 4、使用网络字节输入流FileInputStream对象中的read方法,读取本地文件
int len= 0;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes))!=-1){
// 5、使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
os.write(bytes, 0, len);
}
socket.shutdownOutput();
// 6、使用Socket对象中的方法getInputStream() 获取网络字节输入流InputSteam
InputStream is = socket.getInputStream();
// 7、使用网络字节输入流InputSteam对象中的方法read,读取服务器回写的数据
while ((len = is.read(bytes))!=-1){
System.out.println(new String(bytes, 0, len));
}
// 8、释放资源(Socket)
fis.close();
socket.close();
}
}
异常
异常 Exception
-
异常如何产生的,如何处理异常
- JVM检测出现异常,会做两件事情
- JVM会根据异常产生的原因创建一个异常对象,这个异常对象包含了异常产生的(内容,原因,位置)
- 在出现异常的地方没有异常的处理逻辑(try - catch),那么JVM就会把异常抛出给方法的调用者来处理这个异常
异常的处理
Java异常的处理五个关键字 try、catch、finally、throw、throws
-
throw
- 作用:可以使用throw关键字在指定的方法中抛出指定的异常
- 使用格式:
throw new xxxException(“异常产生的原因”) - 注意事项:
- throw关键字必须写在方法内部
- throw关键字后边new的异常必须是Exception 或 Exception的子类
- throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
throw 关键字后边创建的是 RuntimeException 或者是 RuntimeException 的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象, 中断程序)
throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws, 要么try…catch
-
Objects 非空判断
Objects 中的静态方法 ,查看对象是否为null
public static <T> T requireNonNull(T obj){
if (obj == null)
throw new NullPointerException();
return obj
}
-
throws关键字:异常处理的第一种方式,交给别人处理
作用:
1. 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
2. 可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者(自己不处理,别人处理),最终交给JVM处理—>中断处理
使用格式:在方法声明时使用
修饰符 返回值类型 方法名(参数列表)throws AAAException{
throw new AAAException(“产生原因”)
}注意:
1. throws关键字必须写在方法声明处
2. throws关键字的异常必须是Exception 或 Exception的子类
3. 方法内部如果抛出了多个异常对象,那么throws后边必须声明多个异常。如果抛出的多个异常对象之间有继承关系,那么直接声明父类异常即可
4. 调用了一个声明抛出异常方法,我们就必须处理声明的异常。要么throws, 要么try…catch -
捕获异常 try…catch:异常处理的第二种方式,自己处理异常。
注意:
1. try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
2. 如果try中产生了异常,阿么就会执行catch中的异常处理逻辑,执行catch中的处理逻辑,继续运行之后的代码
- throwable类中定义的三个异常处理的方法
- getMessage 返回此throwable的简短描述
- toString 返回此throwable的详细信息字符串
- printStackTrace JVM 打印异常对象,默认此方法,打印的异常信息是最全面的
- 异常注意事项
- 多个异常分别处理
- 多个异常一次捕获,多次处理
- 多个异常一次捕获一次处理
-
一个try多个catch注意事项:
- catch里面定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上班,否则会报错’
-
运行时异常被抛出可以不处理。既不捕获也不声明,默认给JVM处理,中断程序。
-
如果finally中有return语句,永远返回的是finally中的结果,避免这种情况出现
-
如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是它的子集
-
父类方法没有抛出异常,子类覆盖父类该方法是也可以不抛出异常。此时子类产生异常,只能捕获处理,不能声明抛出
-
在try catch 可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。
自定义异常
- 自定义异常类一般都是以Exception结尾,说明该类是一个异常类
- 自定义异常类,必须继承Exception或者RuntimeException
- 继承Exception: 那么自定义异常类就是编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么 throws,要么try catch
- 继承RuntimeException :那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断)