【2019.10.08】Java 网络编程与异常的用法

网络通信概述

  • C/S 结构:客户端服务器
  • B/S 结构:浏览器服务器

网络编程:在一定的协议下,实现两台计算机的通信的程序

  • 网络通信协议:连接和通信的规则

  • TCP/IP协议:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qga79mqh-1570465781187)(en-resource://database/7806:1)]

  • 协议分类:

    • UDP 用户数据报协议。无连接通信协议。
      • 耗资小,通信效率高。不能保证数据的完整性
    • TCP 传输控制协议。面向连接的通信协议。
      • 创建 三次握手
        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPviEQAA-1570465781189)(en-resource://database/7808:1)]
      • 断开 四次挥手
  • 网络编程三要素

    • 协议:网络通信遵守的规则
    • 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检测出现异常,会做两件事情
    1. JVM会根据异常产生的原因创建一个异常对象,这个异常对象包含了异常产生的(内容,原因,位置)
    2. 在出现异常的地方没有异常的处理逻辑(try - catch),那么JVM就会把异常抛出给方法的调用者来处理这个异常

异常的处理

Java异常的处理五个关键字 try、catch、finally、throw、throws

  1. throw

    • 作用:可以使用throw关键字在指定的方法中抛出指定的异常
    • 使用格式:
      throw new xxxException(“异常产生的原因”)
    • 注意事项:
      1. throw关键字必须写在方法内部
      2. throw关键字后边new的异常必须是Exception 或 Exception的子类
      3. throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
        throw 关键字后边创建的是 RuntimeException 或者是 RuntimeException 的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象, 中断程序)
        throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws, 要么try…catch
  2. Objects 非空判断
    Objects 中的静态方法 ,查看对象是否为null

public static <T> T requireNonNull(T obj){
    if (obj == null)
        throw new NullPointerException();
    return obj
}
  1. throws关键字:异常处理的第一种方式,交给别人处理
    作用:
    1. 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
    2. 可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者(自己不处理,别人处理),最终交给JVM处理—>中断处理
    使用格式:在方法声明时使用
    修饰符 返回值类型 方法名(参数列表)throws AAAException{
    throw new AAAException(“产生原因”)
    }

    注意:
    1. throws关键字必须写在方法声明处
    2. throws关键字的异常必须是Exception 或 Exception的子类
    3. 方法内部如果抛出了多个异常对象,那么throws后边必须声明多个异常。如果抛出的多个异常对象之间有继承关系,那么直接声明父类异常即可
    4. 调用了一个声明抛出异常方法,我们就必须处理声明的异常。要么throws, 要么try…catch

  2. 捕获异常 try…catch:异常处理的第二种方式,自己处理异常。
    注意:
    1. try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
    2. 如果try中产生了异常,阿么就会执行catch中的异常处理逻辑,执行catch中的处理逻辑,继续运行之后的代码

  • throwable类中定义的三个异常处理的方法
    1. getMessage 返回此throwable的简短描述
    2. toString 返回此throwable的详细信息字符串
    3. printStackTrace JVM 打印异常对象,默认此方法,打印的异常信息是最全面的
  1. 异常注意事项
    1. 多个异常分别处理
    2. 多个异常一次捕获,多次处理
    3. 多个异常一次捕获一次处理
  • 一个try多个catch注意事项:

    • catch里面定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上班,否则会报错’
  • 运行时异常被抛出可以不处理。既不捕获也不声明,默认给JVM处理,中断程序。

  • 如果finally中有return语句,永远返回的是finally中的结果,避免这种情况出现

  • 如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是它的子集

  • 父类方法没有抛出异常,子类覆盖父类该方法是也可以不抛出异常。此时子类产生异常,只能捕获处理,不能声明抛出

  • 在try catch 可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。

自定义异常

  1. 自定义异常类一般都是以Exception结尾,说明该类是一个异常类
  2. 自定义异常类,必须继承Exception或者RuntimeException
    1. 继承Exception: 那么自定义异常类就是编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么 throws,要么try catch
    2. 继承RuntimeException :那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值