day12-网络编程

day12-网络编程

今日目标

  • 网络编程
  • TCP通信
  • Junit单元测试
  • 单例设计模式
  • 多例设计模式
  • 工厂设计模式

1 网络编程

1.1 软件架构

  • C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件
  • B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
  • 两种架构各有优势,但是都离不开网络的支持。网络编程 , 就是在一定的协议下,实现两台计算机的通信的程序
  • image-20210418000257329

1.2 什么是网络编程

  • 在网络通信协议下,不同计算机上运行的程序,可以进行数据传输

1.3 网络编程三要素

  • IP地址 : 设备在网络中的地址,是唯一的标识。
  • 端口 : 设备在网络中的地址,是唯一的标识。
  • 数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。

1.4 IP地址

  • IP:全称”互联网协议地址”,也称IP地址。是分配给上网设备的数字标签。常见的IP分类为:ipv4和ipv6

    简单来说 : 就是设备在网络中的唯一标识 , 想要连接哪一台电脑 , 就找到此电脑在网络中的ip地址

  • IP地址常见分类 : ipv4和ipv6

  • 常用命令:

    • ipconfig:查看本机IP地址
    • IP地址:检查网络是否连通
  • 特殊IP地址:

    • 127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用
  • 为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress 供我们使用
    InetAddress:此类表示Internet协议(IP)地址

    • static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址
      String getHostName() 获取此 IP 地址的主机名
      String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
/*
* 演示:InetAddress的基本使用
* 封装IP对象:
* 	static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
* 获取IP和主机信息:
* 	String getHostAddress()返回 IP 地址字符串(以文本表现形式)。 
* 	String getHostName()获取此 IP 地址的主机名。
*/
public class InetAddressDemo {
	public static void main(String[] args) throws Exception {
		// static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
		// suoge表示我的电脑主机名字
		InetAddress ia = InetAddress.getByName("suoge");// suoge/192.168.100.107
		// InetAddress ia = InetAddress.getByName("192.168.100.107");//
		// /192.168.100.107
		// System.out.println(ia);
		// String getHostAddress()返回 IP 地址字符串(以文本表现形式)。
		String ip = ia.getHostAddress();
		// String getHostName()获取此 IP 地址的主机名。
		String hostName = ia.getHostName();
		/*
		 * InetAddress ia = InetAddress.getByName("suoge"):
		 * 192.168.100.107=====suoge
		 * 说明当使用主机名作为函数参数时将计算机名和IP地址同时封装在该对象中,此时调用getHostName获取IP 地址的主机名时,就会获取到指定的计算机名suoge
		 * ================================================================
		 * InetAddress ia = InetAddress.getByName("192.168.100.107"):
		 * 192.168.100.107=====192.168.100.107
		 * 说明当ip地址作为函数参数时没有将计算机名封装在该对象中,此时调用getHostName获取IP 地址的主机名 时,就会获取到指定的IP
		 */
		System.out.println(ip + "=====" + hostName);
		System.out.println("-------------------------");
	}
}

1.5 端口

  • 端口:应用程序在设备中唯一的标识。

  • 端口号:应用程序的唯一标识方式 , 用两个字节表示的整数,它的取值范围是0~65535。
    其中0~1023之间的端口号用于一些知名的网络服务或者应用。
    我们自己使用1024以上的端口号就可以了。

  • 注意:一个端口号只能被一个应用程序使用。

1.6 通信协议

  • 协议:计算机网络中,连接和通信的规则被称为网络通信协议
  • UDP协议
    • 用户数据报协议(User Datagram Protocol)
    • UDP是面向无连接通信协议。
    • 速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。
  • TCP协议
    • 传输控制协议 (Transmission Control Protocol)
    • TCP协议是面向连接的通信协议。
    • 速度慢,没有大小限制,数据安全

2 TCP通信

2.1 TCP发送数据

package com.itheima.tcp_demo.demo1;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/*
    客户端 :

    发送数据的步骤
        1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
            参数说明:
            host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
            port 表示服务器端的端口

        2 通获Socket对象取网络中的输出流,写数据
            OutputStream getOutputStream()

        3 释放资源
            void close()

 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端的Socket对象(Socket) 与指定服务端连接
        Socket socket = new Socket("127.0.0.1", 10010);

        // 通获Socket对象取网络中的输出流,写数据
        OutputStream os = socket.getOutputStream();
        os.write("hello".getBytes());

        // while(true){}

        // 释放资源
        os.close();
        socket.close();
    }
}

2.2 TCP接收数据

package com.itheima.tcp_demo.demo1;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
     服务端接收数据 :

    1 创建服务器端的Socket对象 : ServerSocket类
        ServerSocket(int port)  : 构造方法需要绑定一个端口号 , port就是端口号

    2 监听客户端连接,并接受连接,返回一个Socket对象
        Socket accept() : 该方法会一直阻塞直到建立连接

    3 获取网络中的输入流,用来读取客户端发送过来的数据
        InputStream getInputStream()

    4 释放资源 : 服务端一般不会关闭
        void close()
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
//        1 创建服务器端的Socket对象 : ServerSocket类
//        ServerSocket(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
        ServerSocket serverSocket = new ServerSocket(10010);

//        2 监听客户端连接,并接受连接,返回一个Socket对象
//        Socket accept() : 该方法会一直阻塞直到建立连接
        Socket socket = serverSocket.accept();
//
//        3 获取网络中的输入流,用来读取客户端发送过来的数据
//        InputStream getInputStream()
        InputStream is = socket.getInputStream();
        int by;
        System.out.println("read方法执行前");
        while ((by = is.read()) != -1) {
            System.out.print((char) by);
        }
        System.out.println("read方法执行后");
    }
}

2.3 TCP通信原理分析

image-20210418001010941

2.4 TCP三次握手

image-20210418001256988

2.5 TCP练习1

package com.itheima.tcp_demo.test1;

import java.io.*;
import java.net.Socket;

/*
    客户端 :

    发送数据的步骤
        1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
            参数说明:
            host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
            port 表示服务器端的端口

        2 通获Socket对象取网络中的输出流,写数据
            OutputStream getOutputStream​()

        3 释放资源
            void close​()

 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端的Socket对象(Socket) 与指定服务端连接
        Socket socket = new Socket("127.0.0.1", 10010);

        // 通获Socket对象取网络中的输出流,写数据
        OutputStream os = socket.getOutputStream();
        os.write("hello".getBytes());
        // 像服务端写入结束标记
        socket.shutdownOutput();

        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line = br.readLine();
        System.out.println(line);

        // 释放资源
        br.close();
        os.close();
        socket.close();
    }
}

package com.itheima.tcp_demo.test1;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/*
     服务端接收数据 :

    1 创建服务器端的Socket对象 : ServerSocket类
        ServerSocket​(int port)  : 构造方法需要绑定一个端口号 , port就是端口号

    2 监听客户端连接,并接受连接,返回一个Socket对象
        Socket accept​() : 该方法会一直阻塞直到建立连接

    3 获取网络中的输入流,用来读取客户端发送过来的数据
        InputStream getInputStream​()

    4 释放资源 : 服务端一般不会关闭
        void close​()
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
//        1 创建服务器端的Socket对象 : ServerSocket类
//        ServerSocket​(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
        ServerSocket serverSocket = new ServerSocket(10010);

//        2 监听客户端连接,并接受连接,返回一个Socket对象
//        Socket accept​() : 该方法会一直阻塞直到建立连接
        Socket socket = serverSocket.accept();
//
//        3 获取网络中的输入流,用来读取客户端发送过来的数据
//        InputStream getInputStream​()
        InputStream is = socket.getInputStream();
        int by;

        while ((by = is.read()) != -1) {
            System.out.print((char) by);
        }

        BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bos.write("你谁啊");

        bos.close();
        is.close();
        socket.close();
        serverSocket.close();
    }
}

2.6 TCP练习2

package com.itheima.tcp_demo.test2;

import java.io.*;
import java.net.Socket;

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket socket = new Socket("127.0.0.1", 10086);

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg"));

        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        int by;
        while ((by = bis.read()) != -1) {// 从本地中读一个字节
            bos.write(by);// 往服务器写一个字节
            bos.flush();
        }
        // 写结束标记
        socket.shutdownOutput();

        // 把网络中的字节输入流 , 封装成高效的字符输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//        String line;
//        while ((line = br.readLine()) != null) {
//            System.out.println(line);
//        }
        String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine
        System.out.println(msg);

        // 释放资源
        bis.close();
        socket.close();
    }
}

package com.itheima.tcp_demo.test2;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务端的连接对象
        ServerSocket serverSocket = new ServerSocket(10086);
        Socket socket = null;
        BufferedInputStream bis = null;
        BufferedWriter socketBw = null;

        while (true) {
            // 获取Socket对象
            socket = serverSocket.accept();

            // 获取网络中的字节输入流  在封装成高效的字节输入流对象
            bis = new BufferedInputStream(socket.getInputStream());

            // 创建本地的字节输出流 , 封装成高效的字节输出流
            BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\a.jpg"));

            int by;
            while ((by = bis.read()) != -1) {
                bw.write(by);
                bw.flush();
            }

            //关闭本地流
            bw.close();

            // 获取网络中的字节输出流 , 封装成高效的字符输出流
            socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            socketBw.write("谢谢你");
            socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束
            socketBw.flush();
        }

        // 释放资源
//        socketBw.close();
//        bis.close();
//        socket.close();
//        serverSocket.close();
    }
}

2.7 TCP练习3

package com.itheima.tcp_demo.test3;

import java.io.*;
import java.net.Socket;

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket socket = new Socket("127.0.0.1", 10086);

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg"));

        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        int by;
        while ((by = bis.read()) != -1) {// 从本地中读一个字节
            bos.write(by);// 往服务器写一个字节
            bos.flush();
        }
        // 写结束标记
        socket.shutdownOutput();

        // 把网络中的字节输入流 , 封装成高效的字符输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//        String line;
//        while ((line = br.readLine()) != null) {
//            System.out.println(line);
//        }
        String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine
        System.out.println(msg);

        // 释放资源
        bis.close();
        socket.close();
    }
}

package com.itheima.tcp_demo.test3;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务端的连接对象
        ServerSocket serverSocket = new ServerSocket(10086);

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        while (true) {
            // 获取Socket对象
            Socket socket = serverSocket.accept();
            executorService.submit(new ServerThread(socket));
        }

        // 释放资源
//        socketBw.close();
//        bis.close();
//        socket.close();
//        serverSocket.close();
    }
}

package com.itheima.tcp_demo.test3;

import javax.management.relation.RoleUnresolved;
import java.io.*;
import java.net.Socket;
import java.util.UUID;

public class ServerThread implements Runnable {
    Socket socket = null;
    BufferedOutputStream bw = null;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        try {
            // 获取网络中的字节输入流  在封装成高效的字节输入流对象
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

            // 创建本地的字节输出流 , 封装成高效的字节输出流
            bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\" + UUID.randomUUID() + ".jpg"));

            int by;
            while ((by = bis.read()) != -1) {
                bw.write(by);
                bw.flush();
            }
            // 获取网络中的字节输出流 , 封装成高效的字符输出流
            BufferedWriter socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            socketBw.write("谢谢你");
            socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束
            socketBw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭本地流
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3 单例设计模式

1.作用

​ 单例设计模式的意思是,一个类只允许创建一个实例,也就是一个对象,对象在堆内存中只能开辟一个空间。

2.实现步骤

  1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static final类型的成员变量。
  3. 定义一个静态方法返回这个唯一对象。

3. 单例设计模式的类型

根据实例化对象的时机单例设计模式又分为以下两种:

  1. 饿汉单例设计模式

    饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。

  2. 懒汉单例设计模式

    懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才实例化出对象。不着急,故称为“懒汉模式”。

4.代码实现

  • 饿汉式

    • 比较饥饿,着急创建出来唯一的一个对象

      /*
      	单例设计模式
          比较饥饿,着急创建出来唯一的一个对象
      */
      public class Single
      {
      	//1、私有化构造函数
      	private Single(){}
      	//2、创建一个本类的对象
      	private static final Single s=new Single();
      	//3、定义一个方法返回本类的对象
      	public static Single getInstance()
      	{
      		return s;
      	}
      	//测试方法
      	public void test()
      	{
      		System.out.println("测试方法");
      	}
      }
      class SingleDemo 
      {
      	public static void main(String[] args) 
      	{
      		/*
      			要想获取到Single类的对象,调用getInstance方法
      			既然不能通过对象来调用,那么只能通过类名来调用
      			如果要想通过类名来调用方法,那么被调用的方法必须用static来修饰
      		*/
      		Single s1=Single.getInstance();
      		s1.test();
      		//Single s2=Single.getInstance();
      	}
      }
      
  • 懒汉式

    • 比较懒惰,什么时候用对象,就什么时候创建

      public class Single {
          //比较懒惰,什么时候用对象,就什么时候创建
      
          //构造方法私有化
          private Single(){
      
          }
      
          //创建一个唯一的对象
          private static Single s = null;
      
          //获取对象的方法
          public static synchronized Single getSingle(){
              //如果值是null,说明没有创建过这个对象
              if(s == null){//t1 t2
                  s = new Single();
              }
      
              return s;
          }
      }
      

注意:懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态,所以加上关键字:synchronized,保证其同步安全。

5. 小结

单例模式可以保证系统中一个类只有一个对象实例。

实现单例模式的步骤:

  1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static final类型的成员变量。
  3. 定义一个静态方法返回这个唯一对象。

4 多例设计模式

多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。

  • 作用

    ​ 一个类可以创建多个对象,有多个实例。

  • 实现步骤

    ​ 1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。

    ​ 2.在类中定义存放类实例的list集合

    ​ 3.在类中提供静态代码块,在静态代码块中创建类的实例

    ​ 4.提供获取类实例的静态方法

  • 代码演示

    • 比如一个类只允许创建3个对象。

      public class Person {
          //这个类一共创建3个对象
      
          //构造方法私有化
          private Person(){}
      
          //定义集合用于保存多个对象
          private static ArrayList<Person> list = new ArrayList<>();
      
          //静态代码块,只会执行一次,且是在这个类最开始最先执行
          static{
              //创建3个对象放在集合里
              for (int i = 0; i < 3; i++) {
                  list.add(new Person());
              }
          }
      
          //定义供外界访问的获取对象的方法
          public static Person getPerson(){
              //随机一个对象返回给调用者
              //创建随机对象
              Random r = new Random();
              //获取索引
              int i = r.nextInt(3);
              //根据索引从集合中获取对象
              Person person = list.get(i);
              //返回给调用者
              return person;
          }
      }
      
      public class Demo {
          public static void main(String[] args) {
      
              //获取10次,但是获取到的其实就是3个对象
              for (int i = 0; i < 10; i++) {
                  Person person = Person.getPerson();
                  System.out.println(person);
              }
          }
      }
      

5 工厂设计模式

  • 介绍

    工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.

  • 没有使用工厂设计模式创建对象即以前创建对象方式的问题

    • 假设定义三个车类 BaoMa BenChi AoDi,然后在测试类创建三个类的对象

    • 代码演示:

      public class BaoMa {
      }
      public class BenChi {
      }
      public class AoDi {
      }
      public class Test01 {
          public static void main(String[] args) {
              //创建三个类的对象
              BaoMa baoMa = new BaoMa();
              BenChi benChi = new BenChi();
              AoDi aoDi = new AoDi();
          }
      }
      

      问题说明:

      ​ 1.假设当前项目下具有很多个测试类,都要使用这三个类,然后我想统计每个类的对象有多少个,那么 这样统计就会很麻烦

      ​ 2.测试类和这三个类还耦合在一起了,我们应该降低耦合

  • 使用工厂设计模式创建对象

    • 实现步骤
      • 编写一个Car接口
      • 编写一个BaoMa类实现Car接口
      • 编写一个Benchi类实现Car接口
      • 编写一个AoDi类实现Car接口
      • 提供一个CarFactory(汽车工厂),定义静态方法用于生产汽车对象
      • 定义CarFactoryTest测试汽车工厂
  • 代码演示

    package com.itheima.sh.demo_04;
    /*
        汽车工厂类
     */
    public class CarFactory {
        //工厂专门用来创建汽车对象
        //name表示调用该方法时指定的车类型
        public static Car getCar(String name){
            if(name.equals("BaoMa")){
                return new BaoMa();
            }else if(name.equals("BenChi")){
                return new BenChi();
            }else if(name.equals("AoDi")){
                return new AoDi();
            }
    
            return null;
        }
    }
    //汽车
    public interface Car {
    }
    public class BaoMa implements Car{
         //宝马
    }
    public class BenChi implements Car{
        //奔驰
    }
    public class AoDi implements Car{
        //奥迪
    }
    //测试类
    public class Test01 {
        public static void main(String[] args) {
            //创建三个类的对象
    //        BaoMa baoMa = new BaoMa();
    //        BenChi benChi = new BenChi();
    //        AoDi aoDi = new AoDi();
    
            //---------------------------------
            //使用工厂创建对象
            Car baoMa = CarFactory.getCar("BaoMa");
            System.out.println(baoMa);
    
            Car benChi = CarFactory.getCar("BenChi");
            System.out.println(benChi);
    
            Car AoDi = CarFactory.getCar("AoDi");
            System.out.println(AoDi);
        }
    }
    

小结:

  • 工厂模式的存在可以改变创建类的对象方式

  • 方便管理对象

  • 降低类与类之间的耦合,调用工厂类中的方法直接指定字符串即可,创建对象的事情都交给工厂类

posted @   忘了鱼尾纱的猫  阅读(4)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
  1. 1 刘哈哈与大先生 刘心&大鹏
  2. 2 我们打着光脚在风车下跑,手上的狗尾巴草摇啊摇 等一下就回家 / -艾兜
  3. 3 哎呦 毛不易
  4. 4 夜、萤火虫和你 AniFace
夜、萤火虫和你 - AniFace
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

轻音乐

点击右上角即可分享
微信分享提示