day12-Junit单元测试、Socket网络编程

学习目标

- 能够使用Junit进行单元测试
	1.在当前模块下新建lib文件夹,拷贝junit的jar包,把jar包添加到图书馆中
	2.在要执行的方法上添加一个@Test注解
	3.点击方法左边的绿色三角或者右键选择方法,选择run 方法名称运行方法
- 能够辨别UDP和TCP协议特点
	udp:面向无连接的协议,通信双方不用连接连接,可以直接发送数据(视频聊天,视频会议...)
		好处:效率高,耗资小
		弊端:容易丢失数据
	tcp:面向连接的协议,客户端和服务器必须经过3次握手建立逻辑连接,才能通信(文件的上传和下载,发送文件...)
		好处:安全
		弊端:效率低
- 能够说出TCP协议下两个常用类名称
	客户端:Socket
	服务器:ServerSocket  accpet获取客户端
- 能够编写TCP协议下字符串数据传输程序
	笔记中:TCP通信程序(重点)
- 能够理解TCP协议下文件上传案例(重点)
	客户端:读取客户端硬盘上的文件,把文件上传到服务器,读取服务器回写的"上传成功!"
	服务器:读取客户端上传的文件,把文件保存到服务器的硬盘上,给客户端回写"上传成功!"
- 能够理解TCP协议下BS案例(看图)
	1.使用浏览器作为客户端访问服务器的某一个资源(index.html)
	http://localhost:8080/day12/web/index.html==>URL 统一资源定位符
	2.在服务器中获取客户端请求的资源路径(web/index.html)
	3.服务器使用本地字节输入流,根据路径读取index.html文件
	4.服务器在使用网络字节输出流,把读取到的index.html文件,写到客户端浏览器中显示

第一章 Junit单元测试

1.Junit介绍

Junit是一个Java语言的单元测试框架,简单理解为可以用于取代java的(部分)main方法。Junit属于第三方工具,需要导入jar包后使用。

2.Junit的基本使用(重点)

a.在当前模块下创建lib文件夹
在这里插入图片描述

b.把junit的jar包,拷贝到lib的文件夹中
在这里插入图片描述

c.把jar包添加到图书馆中

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.itheima.demo01junit;

import org.junit.Test;

/*
    junit作用:单独的测试某一个方法
    使用步骤:
        1.导入第三方的junit的jar包
        2.在要执行的方法上添加一个@Test注解(需要导包)
        3.点击方法左边的绿色三角或者右键选择方法名称,选择"Run 方法名称",运行方法
          点击类左边的绿色三角或者右键选择类名,选择"Run 类名",可以运行类中所有被@Test修饰的方法
          右键点击模块名称,选择"Run All Tests",可以运行模块中所有类中被@Test修饰的方法
 */
public class Demo01Junit {
   @Test
   public void show01(){
       System.out.println("show01方法");
   }

   @Test
    public void show02(){
        System.out.println("show02方法");
    }

    @Test
    public void show03(){
        System.out.println("show03方法1");
        System.out.println("show03方法2");
        System.out.println("show03方法3");
        System.out.println("show03方法4");
    }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.Junit的注意事项

package com.itheima.demo01junit;

import org.junit.Test;

/*
    Junit的注意事项
    1.没有添加@Test注解的方法,不能使用Junit运行
    2.junit只能运行public修饰的,没有参数,没有返回值的,非静态方法
 */
public class Demo02Junit {
   public void show01(){
       System.out.println("Demo02Junit show01方法");
   }

   //java.lang.Exception: Method show02 should have no parameters 方法show02应该没有参数
   //@Test
    public void show02(int a){
        System.out.println("Demo02Junit show02方法"+a);
    }

    //java.lang.Exception: Method show03() should be void 方法show03应该返回值类型是void
    //@Test
    public String show03(){
        System.out.println("Demo02Junit show03方法");
        return "你好";
    }

    //java.lang.Exception: Method show04() should not be static 方法shwo04应该没有被static修饰
    //@Test
    public static void show04(){
        System.out.println("Demo02Junit show04方法");
    }

    //java.lang.Exception: Method show05() should be public 方法show05应该是public修饰
    //@Test
    void show05(){
        System.out.println("Demo02Junit show05方法");
    }

    /*
        可以定义一个能使用junit运行的方法
        在方法中调用哪些不能运行的方法就可以了
     */
    @Test
    public void method(){
        show01();
        show02(10);
        String s = show03();
        System.out.println(s);
        show04();
        show05();
    }
}

4.Junit相关注解

package com.itheima.demo01junit;

import org.junit.*;

/*
    Junit相关注解
    @Test:
        可以单独执行某一个方法
    @Before:
        用来修饰方法,该方法会在每一个测试方法执行之前[自动]执行一次。
    @After:
        用来修饰方法,该方法会在每一个测试方法执行之后[自动]执行一次
     @BeforeClass:
        用来[静态]修饰方法,该方法会在所有测试方法之前[自动]执行一次,而且只执行一次
     @AfterClass:
        用来[静态]修饰方法,该方法会在所有测试方法之后[自动]执行一次,而且只执行一次
     注意:
        @Before,@After,@BeforeClass,@AfterClass:这四个注解修饰的方法,不能单独执行
        @Before,@After,@BeforeClass,@AfterClass:这四个注解修饰的方法,会自动在@Test修饰的方法前后执行
 */
public class Demo03Junit {
    @Test
    public void show01(){
        System.out.println("show01方法");
    }

    @Test
    public void show02(){
        System.out.println("show02方法");
    }

    @Test
    public void show03(){
        System.out.println("show03方法");
    }

    @Before
    public void before(){
        System.out.println("before方法!");
    }

    @After
    public void after(){
        System.out.println("after方法!");
    }

    @BeforeClass
    public static void beforeClass(){
        System.out.println("beforeClass方法!");
    }

    @AfterClass
    public static void afterClass(){
        System.out.println("afterClass方法!");
    }
}

执行的结果:

beforeClass方法!
before方法!
show01方法
after方法!
before方法!
show02方法
after方法!
before方法!
show03方法
after方法!
afterClass方法!

备注:

Junit常用注解(Junit5.x版本)
* @BeforeEach:用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
* @AfterEach:用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
* @BeforeAll:用来静态修饰方法,该方法会在所有测试方法执行之前执行一次。
* @AfterAll:用来静态修饰方法,该方法会在所有测试方法执行之后执行一次

5.断言

  • 作用
    预测判断某个条件一定成立, 如果条件不成立,则直接奔溃(抛出断言异常)

  • 使用方式
    两个参数: 第一个代表预判值, 第二个代表实际结果
    如果预判正确就会绿色通过
    如果预判错误就会红色失败
    Assert.assertEquals(期望结果 , 实际结果);

package com.itheima.demo01junit;

import org.junit.Assert;

/*
    断言
    - 作用
        预测判断某个条件一定成立,  如果条件不成立,则直接奔溃(抛出断言异常)
    - 使用方式
        两个参数: 第一个代表预判值,  第二个代表实际结果
        如果预判正确就会绿色通过
        如果预判错误就会红色失败
        Assert.assertEquals(期望结果 , 实际结果);
 */
public class Demo04Junit {
    public static void main(String[] args) {
        //断言正常:程序正常执行
        Assert.assertEquals(true,"aaa".length()==3);
        //断言错误:AssertionError: expected:<true> but was:<false> 预期是true,得到的是false
        //Assert.assertEquals(true,"aaaa".length()==3);

        //断言正常:程序正常执行
        Assert.assertEquals(10,Integer.parseInt("10"));
        //断言错误:AssertionError: expected:<10> but was:<11> 预期是10,得到的是11
        //Assert.assertEquals(10,Integer.parseInt("11"));
    }
}

第二章 网络编程入门

网络编程三要素:协议,IP地址,端口号

1.软件结构

C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。

两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。

2.网络通信协议

协议就是规则:就是计算机通信需要遵守的

udp:面向无连接的协议,通信的双方不用建立连接,可以直接发送数据

​ 好处:效率高(速度快),耗资小

​ 弊端:不安全,容易丢失数据

tcp:面向连接协议,客户端和服务器端必须经过3次握手建立逻辑连接,才能通信

​ 好处:安全

​ 弊端:效率低

三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

​ 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。服务器你死了吗?

​ 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。我活着 啊!!

​ 第三次握手,客户端再次向服务器端发送确认信息,确认连接。我知道了!!

在这里插入图片描述

3.IP地址

IP地址:就相当于计算机的身份号(唯一)

ip地址的作用:具有唯一性,在网络中可以通过ip地址找到另外一台计算机
ip地址分类
1.ipv4:ip地址由4个字节组成,一个字节8(比特位1,0)
    二进制:11001101.11001100.11000001.11001111
    为了表示方便使用十进制:192.0.0.106
    每个字节的范围:0-255(2^8),ip地址第一位不能为0   
    ip地址的数量:42亿
    	2^32=4294967296个
    问题:随着计算机的增多,ip地址面临枯竭(全球IPv4地址在20112月分配完毕)不够用,就出了ipv6地址	
2.ipv6:ip地址由16个字节组成,一个字节8(比特位1,0)
    ip地址的数量:
		2^128=3.4028236692093846346337460743177e+38
        3400000000000000000000000000000000000000000000000000000000000000    
    号称可以为地球上每一粒沙子编写一个ip地址
    为了表示方便使用十六进制:fe80::a8a6:b83c:8b8b:2685%17
常用一些dos命令:dos窗口  win+r==>cmd==>dos窗口     
1.ipconfig	Windows IP 配置
   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::a8a6:b83c:8b8b:2685%17
   IPv4 地址 . . . . . . . . . . . . : 192.168.0.106
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : 192.168.0.1
2.ping ip地址:测试你的电脑和指定ip地址的电脑是否可以连通
	ping空格ip地址
	C:\Users\Administrator>ping 192.168.0.222  没有ping通
    正在 Ping 192.168.0.222 具有 32 字节的数据:
    来自 192.168.0.106 的回复: 无法访问目标主机。
    来自 192.168.0.106 的回复: 无法访问目标主机。
    来自 192.168.0.106 的回复: 无法访问目标主机。
    来自 192.168.0.106 的回复: 无法访问目标主机。
    C:\Users\Administrator>ping 192.168.0.107  ping通
    正在 Ping 192.168.0.107 具有 32 字节的数据:
    来自 192.168.0.107 的回复: 字节=32 时间=3ms TTL=64
    来自 192.168.0.107 的回复: 字节=32 时间=3ms TTL=64
    来自 192.168.0.107 的回复: 字节=32 时间=5ms TTL=64
    来自 192.168.0.107 的回复: 字节=32 时间=3ms TTL=64
    C:\Users\Administrator>ping www.baidu.com
    正在 Ping www.a.shifen.com [61.135.169.121] 具有 32 字节的数据:
    来自 61.135.169.121 的回复: 字节=32 时间=6ms TTL=56
    来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
    来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
    来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56 
    ping 127.0.0.1    ping localhost ping本机的ip地址(你自己电脑的ip地址) 

4.端口号

注意:每个网络软件都会分配一个端口号,通过这个端口号就可以找到这个软件

在这里插入图片描述

5.InetAddress类

package com.itheima.demo02InetAddress;

import java.net.InetAddress;
import java.net.UnknownHostException;

/*
    java.net.InetAddress:此类表示互联网协议 (IP) 地址。
    静态方法:
        static InetAddress getLocalHost() 返回本地主机。 你自己的电脑
        static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
    非静态方法:
        String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
        String getHostName() 获取此 IP 地址的主机名。
 */
public class Demo01InetAddress {
    public static void main(String[] args) throws UnknownHostException {
        show02();
    }

    /*
        static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
        参数:
            String host:电脑名,ip地址,域名
        static InetAddress[] getAllByName(String host)      
     */
    private static void show02() throws UnknownHostException {
        //InetAddress inet = InetAddress.getByName("LAPTOP-8I2VG167");//LAPTOP-8I2VG167/192.168.136.48
        //InetAddress inet = InetAddress.getByName("192.168.136.48");//LAPTOP-8I2VG167/192.168.136.48
        //InetAddress inet = InetAddress.getByName("www.baidu.com");//www.baidu.com/182.61.200.6
        InetAddress inet = InetAddress.getByName("www.itheima.com");//www.itheima.com/47.95.137.221
        System.out.println(inet.getHostAddress());
        System.out.println(inet.getHostName());
        System.out.println(inet);
    }

    /*
        static InetAddress getLocalHost() 返回本地主机。 你自己的电脑
        UnknownHostException:未知主机异常
     */
    private static void show01() throws UnknownHostException {
        InetAddress inet = InetAddress.getLocalHost();
        System.out.println(inet);//daofeng/192.168.136.73 打印对象名不是地址,重写了toString方法

        //String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
        String hostAddress = inet.getHostAddress();
        System.out.println(hostAddress);//192.168.136.73
        //String getHostName() 获取此 IP 地址的主机名。
        String hostName = inet.getHostName();
        System.out.println(hostName);//daofeng
    }
}

第三章 TCP通信程序

1.TCP通信的概述

在这里插入图片描述

2.TCP通信的客户端(重点)

package com.itheima.demo03TCP;

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

/*
    创建TCP客户端
    作用:和服务器经过3次握手建立逻辑连接,给服务器发送数据,读取服务器回写的数据
    表示客户端的类:
        java.net.Socket
            此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
            套接字就是封装了IP地址和端口号的网络单位
    构造方法:
        Socket(InetAddress address, int port)创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
        Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
        参数:
            InetAddress address|String host:服务器的ip地址
            int port:服务器的端口号
    成员方法:
        OutputStream getOutputStream()返回此套接字的输出流。
        InputStream getInputStream() 返回此套接字的输入流。
    ----------------------------------------------------------------------------------------------
    注意事项:
        1.当我们创建客户端Socket对象的时候,就会通过服务器的ip地址和端口号找服务器进行三次握手
            a.服务器已经启动,并且ip地址和端口号书写正确,握手成功
            b.服务器没有启动,或者ip地址端口号书写错误,握手失败,抛出连接异常
                ConnectException: Connection refused: connect
        2.客户端和服务器之间进行读写数据必须使用Socket中提供的网络流对象
            Socket中提供的流对象的数据源和目的地就是客户端和服务器
            使用自己创建的流对象,数据源和目的地是本地硬盘
            new FileInputStream("a.txt")  new FileOutputStream("a.txt");  a.txt==>硬盘
    ----------------------------------------------------------------------------------------------
    实现步骤(重点):
        1.创建客户端Socket对象,构造方法绑定服务器的ip地址和端口号(三次握手)
        2.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
        3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        5.使用网络字节输入流InputStream对象中的方法read,读取服务器发送的数据
        6.释放资源(Socket)
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建客户端Socket对象,构造方法绑定服务器的ip地址和端口号(三次握手)
        Socket socket = new Socket("localhost",8888);
        //2.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        System.out.println("①客户端给服务器发送数据:你好服务器");
        os.write("你好服务器".getBytes());
        //4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //5.使用网络字节输入流InputStream对象中的方法read,读取服务器发送的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println("④客户端读取服务器发送的数据:"+new String(bytes,0,len));
        //6.释放资源(Socket)
        socket.close();
    }
}

3.TCP通信服务器端(重点)

package com.itheima.demo03TCP;

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

/*
    TCP通信服务器端(重点)
    作用:和客户端经过3次握手建立连接通路,读取客户端发送的数据,给客户端回写数据
    表示服务器的类:
        java.net.ServerSocket:此类实现服务器套接字。
    构造方法:
        ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
    成员方法:
        Socket accept() 侦听并接受到此套接字的连接。
        服务器调用accpet方法,就会出现监听状态,一直等待客户端连接服务器
        获取客户端Socket对象
        没有客户端连接服务器,此方法会阻塞(一直等待)
    ---------------------------------------------------------------------------------------------
    实现步骤(重点):
        1.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
        2.使用服务器ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
        3.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        4.使用网络字节输入流InputStream对象中的方法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对象中的方法accpet,监听并获取客户端Socket对象
        System.out.println("----------------服务器已经启动,等待客户端连接--------------------");
        Socket socket = server.accept();
        //3.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //4.使用网络字节输入流InputStream对象中的方法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,给客户端发送数据
        System.out.println("③服务器给客户端发送数据:收到谢谢");
        os.write("收到谢谢".getBytes());
        //7.释放资源(Socket,ServerSocket)
        socket.close();
        server.close();
    }
}

服务器启动之后,服务器的accpet方法一直处于监听状态,等待客户端连接

在这里插入图片描述

4.TCP通信的流程

在这里插入图片描述

第四章 综合案例

1.文件上传案例需求分析

在这里插入图片描述

2.文件上传的客户端(重点)

package com.itheima.demo04TCPUpload;

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

/*
    文件上传的客户端(重点)
    作用:读取本地要上传的文件,把文件上传到服务器,读取服务器回写的"上传成功"
    文件的上传就是文件的复制:
        数据源: c:\\1.jpg
        目的地: 服务器
    实现步骤:
         1.创建本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
         2.创建客户端Socket对象,构造方法中封装服务器ip地址和端口号(3次握手)
         3.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
         4.使用本地字节输入流FileInputStream对象中的方法read,读取要上传的文件
         5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传服务器
         6.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
         7.使用网络字节输入流InputStream对象中的read,读取服务器回写的"上传成功!"
         8.释放资源(fis,Socket)
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        //2.创建客户端Socket对象,构造方法中封装服务器ip地址和端口号(3次握手)
        Socket socket = new Socket("127.0.0.1",9999);
        //3.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //4.使用本地字节输入流FileInputStream对象中的方法read,读取要上传的文件
        byte[] bytes= new byte[1024];
        int len = 0;
        while ((len=fis.read(bytes))!=-1){
            //5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传服务器
            os.write(bytes,0,len);
        }

        /*
            解决:客户端写完文件之后,给服务器写一个结束标记
            void shutdownOutput() 禁用此套接字的输出流。
            对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列(结束标记)。
         */
        socket.shutdownOutput();

        //6.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //7.使用网络字节输入流InputStream对象中的read,读取服务器回写的"上传成功!"
        System.out.println("333333333333333333333333333333333");
        while ((len=is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }
        System.out.println("444444444444444444444444444444444");
        //8.释放资源(fis,Socket)
        fis.close();
        socket.close();
    }
}

3.文件上传的服务器端(重点)

package com.itheima.demo04TCPUpload;

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

/*
    文件上传的服务器端(重点)
    作用:读取客户端上传的文件,把文件保存到服务器的硬盘上,给客户端回写"上传成功!"
    文件的上传就是文件的复制:
        数据源: 客户端上传的文件
        目的地: 服务器的硬盘上  d:\\upload\\1.jpg
    实现步骤:
         1.判断d盘是否有upload文件夹,没有则创建
         2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
         3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
         4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
         5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
         6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
         7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
         8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
         9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
         10.释放资源(fos,socket,server)
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.判断d盘是否有upload文件夹,没有则创建
        File file = new File("d:\\upload");
        if(!file.exists()){
            file.mkdir();
        }
        //2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
        ServerSocket server = new ServerSocket(9999);
        //3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
        System.out.println("--------------服务器已经启动,等待客户端上传文件---------------");
        Socket socket = server.accept();
        //4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
        FileOutputStream fos = new FileOutputStream("d:\\upload\\1.jpg");
        //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
        byte[] bytes = new byte[1024];
        int len = 0;
        System.out.println("11111111111111111111111111111111111");
        while ((len=is.read(bytes))!=-1){
            //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
            fos.write(bytes,0,len);
        }
        System.out.println("2222222222222222222222222222222222222");
        //8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
        os.write("上传成功!".getBytes());
        //10.释放资源(fos,socket,server)
        fos.close();
        socket.close();
        server.close();
    }
}

问题:客户端没有读取到服务器回写的"上传成功",服务器和客户端都没有执行结束

在这里插入图片描述

4.文件上传的阻塞问题(重点)

在这里插入图片描述

/*
    解决:客户端写完文件之后,给服务器写一个结束标记
    void shutdownOutput() 禁用此套接字的输出流。
    对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列(结束标记)。
 */
socket.shutdownOutput();

5.文件上传自定义文件名称命名规则(了解-扩展)

package com.itheima.demo05TCPUpload;

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

/*
    文件上传的服务器端(重点)
    作用:读取客户端上传的文件,把文件保存到服务器的硬盘上,给客户端回写"上传成功!"
    文件的上传就是文件的复制:
        数据源: 客户端上传的文件
        目的地: 服务器的硬盘上  d:\\upload\\1.jpg
    实现步骤:
         1.判断d盘是否有upload文件夹,没有则创建
         2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
         3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
         4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
         5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
         6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
         7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
         8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
         9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
         10.释放资源(fos,socket,server)
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.判断d盘是否有upload文件夹,没有则创建
        File file = new File("d:\\upload");
        if(!file.exists()){
            file.mkdir();
        }
        //2.创建服务器ServerSocket对象,构造方法和系统要指定的端口号
        ServerSocket server = new ServerSocket(9999);
        //3.使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
        System.out.println("--------------服务器已经启动,等待客户端上传文件---------------");
        Socket socket = server.accept();
        //4.使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();

        /*
            自定义一个文件的名称:规则==>不重复
            域名+毫秒值+随机数
         */
        String fileName = "itheima"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

        //5.创建本地字节输出流FileOutputStream对象,构造方法中邦迪要写入目的地
        FileOutputStream fos = new FileOutputStream("d:\\upload\\"+fileName);
        //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
        byte[] bytes = new byte[1024];
        int len = 0;
        System.out.println("11111111111111111111111111111111111");
        while ((len=is.read(bytes))!=-1){
            //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
            fos.write(bytes,0,len);
        }
        System.out.println("2222222222222222222222222222222222222");
        //8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
        os.write("上传成功!".getBytes());
        //10.释放资源(fos,socket,server)
        fos.close();
        socket.close();
        server.close();
    }
}

6.多线程版本服务器(了解-扩展)

package com.itheima.demo06ThreadUpload;

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

/*
    多线程版本的服务器:
        1.定义一个死循环.轮询accpet方法,一直获取客户端Socket对象
        2.有客户端请求服务器,就开启一个新的线程,完成文件上传
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        File file = new File("d:\\upload");
        if(!file.exists()){
            file.mkdir();
        }
        ServerSocket server = new ServerSocket(9999);
        System.out.println("--------------服务器已经启动,等待客户端上传文件---------------");

        //1.定义一个死循环.轮询accpet方法,一直获取客户端Socket对象
        while (true){
            Socket socket = server.accept();

            /*
                Socket对象中的方法
                    InetAddress getInetAddress() 获取上传文件客户端ip地址对象
             */
            System.out.println("当前上传文件的客户端是:"+socket.getInetAddress().getHostAddress());

            //2.有客户端请求服务器,就开启一个新的线程,完成文件上传
            new Thread(()->{
                try {
                    InputStream is = socket.getInputStream();

                    String fileName = "itheima"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

                    FileOutputStream fos = new FileOutputStream("d:\\upload\\"+fileName);
                    byte[] bytes = new byte[1024];
                    int len = 0;
                    while ((len=is.read(bytes))!=-1){
                        fos.write(bytes,0,len);
                    }
                    OutputStream os = socket.getOutputStream();
                    os.write("上传成功!".getBytes());
                    fos.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        //server.close();
    }
}

7.模拟BS服务器(了解)

模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果。

案例分析

  1. 准备页面数据,web文件夹。

    <!DOCTYPE html>
    <html lang="zh">
    <head>
        <meta charset="UTF-8">
        <title>我的主页</title>
    </head>
    <body>
        <h1>我的主页</h1>
    </body>
    </html>
    

在这里插入图片描述

  1. 我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问,查看网页效果
package com.itheima.demo06BSTCP;

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

/*
    BS版本的TCP案例
    B:浏览器(作为客户端)
    S:服务器
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //创建服务器ServerSocket对象,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8080);
        //使用ServerSocket对象accept,监听并获取客户端Socket对象
        Socket socket = server.accept();
        //使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //使用字节输入流InputStream对象中的方法read,读取客户端的请求信息
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        //释放资源
        socket.close();
        server.close();
    }
}

http://localhost:8080/day12/web/index.html

在这里插入图片描述

代码实现

BS服务器
package com.itheima.demo07BSTCP;

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

/*
    BS版本的服务器
    服务器要完成的事情:根据文件的相对路径,创建本地字节输入流读取index.html文件,把index.html写到客户端浏览器显示出来
    1.把网络字节输入流InputStream转换为BufferedReader
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
    2.使用BufferedReader中的方法readLine,读取第一行文本
        String s = "GET /day12/web/index.html HTTP/1.1";
    3.使用String类的方法split,根据空格截取字符串,返回一个字符串数组
    4.只要数组的arr[1] "/day12/web/index.html "
    5.使用String类的方法subString,从1索引开始截取字符串 "day12/web/index.html"
    6.创建本地字节输入流,根据文件的相对路径,读取文件
    7.使用网络字节输出流OutputStream,把读取到的文件,发送到客户端显示
 */
public class TCPServer2 {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象和系统要指定的端口号
        ServerSocket server = new ServerSocket(8080);
        //使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
        Socket socket = server.accept();
        //使用Socket对象获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //1.把网络字节输入流InputStream转换为BufferedReader
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        //2.使用BufferedReader中的方法readLine,读取第一行文本
        String s = br.readLine();//"GET /day12/web/index.html HTTP/1.1";
        //3.使用String类的方法split,根据空格截取字符串,返回一个字符串数组
        String[] arr = s.split(" ");
        //4.只要数组的arr[1] "/day12/web/index.html "
        //5.使用String类的方法subString,从1索引开始截取字符串 "day12/web/index.html"
        String path = arr[1].substring(1);
        //6.创建本地字节输入流,根据文件的相对路径,读取文件
        FileInputStream fis = new FileInputStream(path);
        //7.使用网络字节输出流OutputStream,把读取到的文件,发送到客户端显示
        OutputStream os = socket.getOutputStream();

        //增加以下三行代码的目的:告之客户端浏览器,写回的是html类型的文件,让客户端以网页的形式显示文件,而不是文本
        os.write("HTTP/1.1 200 OK\r\n".getBytes());
        os.write("Content-Type:text/html\r\n".getBytes());
        os.write("\r\n".getBytes());

        //一读一写复制文件
        byte[] bytes =  new byte[1024];
        int len = 0;
        while ((len=fis.read(bytes))!=-1){
            os.write(bytes,0,len);
        }
        fis.close();
        socket.close();
        server.close();
    }
}

c.index.html页面添加多个图片,多次请求服务器

在这里插入图片描述

package com.itheima.demo07BSTCP;

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

/*
    BS版本的服务器
    问题:
        服务器给客户端写的页面中包含图片
        服务器仅仅是把index.html页面写给客户端,并没有把图片一起写回
        客户端:找不到图片
        客户端显示index.html页面包含图片的路径<img src="img/logo2.png">
        客户端会自动根据图片的路径,再一次请求服务器,让服务器根据路径读取图片,写回到客户端
 */
public class TCPServer3 {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象和系统要指定的端口号
        ServerSocket server = new ServerSocket(8080);

        //1.增加一个死循环,轮询accpet方法,一直监听客户端的请求
        while (true){
            //使用ServerSocket对象中的方法accpet,监听并获取客户端Socket对象
            Socket socket = server.accept();
            //2.有客户端请求服务器,获取到客户端,开启一个新的线程,根据客户端发送的路径,读取文件,给服务器写回
            new Thread(()->{
                try {
                    //使用Socket对象获取网络字节输入流InputStream对象
                    InputStream is = socket.getInputStream();
                    //1.把网络字节输入流InputStream转换为BufferedReader
                    BufferedReader br = new BufferedReader(new InputStreamReader(is));
                    //2.使用BufferedReader中的方法readLine,读取第一行文本
                    String s = br.readLine();//"GET /day12/web/index.html HTTP/1.1";
                    //3.使用String类的方法split,根据空格截取字符串,返回一个字符串数组
                    String[] arr = s.split(" ");
                    //4.只要数组的arr[1] "/day12/web/index.html "
                    //5.使用String类的方法subString,从1索引开始截取字符串 "day12/web/index.html"
                    String path = arr[1].substring(1);
                    System.out.println(path);
                    //6.创建本地字节输入流,根据文件的相对路径,读取文件
                    FileInputStream fis = new FileInputStream(path);
                    //7.使用网络字节输出流OutputStream,把读取到的文件,发送到客户端显示
                    OutputStream os = socket.getOutputStream();

                    //增加以下三行代码的目的:告之客户端浏览器,写回的是html类型的文件,让客户端以网页的形式显示文件,而不是文本
                    os.write("HTTP/1.1 200 OK\r\n".getBytes());
                    os.write("Content-Type:text/html\r\n".getBytes());
                    os.write("\r\n".getBytes());

                    //一读一写复制文件
                    byte[] bytes =  new byte[1024];
                    int len = 0;
                    while ((len=fis.read(bytes))!=-1){
                        os.write(bytes,0,len);
                    }
                    fis.close();
                    socket.close();
                } catch (IOException e) {
                    System.out.println("您发送的路径找不到!");
                }
            }).start();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JUnit-BOM(Bill of Materials)是JUnit团队为JUnit 5框架提供的一个特殊模块。它的目的是简化JUnit 5的依赖管理。通过添加JUnit-BOM到你的项目中,你可以使用JUnit 5的所有核心模块和扩展模块,而无需单独指定每个模块的版本。 要使用JUnit-BOM,你需要在你的项目的构建文件中添加一个依赖项。对于Maven项目,可以在`<dependencies>`标签中添加以下内容: ``` <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.x.x</version> <!-- 替换为你想要使用的JUnit 5版本 --> <type>pom</type> <scope>import</scope> </dependency> ``` 对于Gradle项目,可以在`dependencies`块中添加以下内容: ``` dependencies { testImplementation platform('org.junit:junit-bom:5.x.x') // 替换为你想要使用的JUnit 5版本 testImplementation 'org.junit.jupiter:junit-jupiter-api' // 添加其他所需的JUnit 5模块 } ``` 添加JUnit-BOM之后,你可以使用JUnit 5的各种功能,例如编写和运行JUnit单元测试。你可以使用`@Test`注解来标记测试方法,并使用JUnit提供的各种断言和注解来编写强大的单元测试。 通过引用JUnit-BOM,你可以确保你的项目使用与JUnit 5兼容的版本,并且可以轻松管理依赖关系。这使得在不同的项目中使用JUnit 5变得更加简单和一致。 JUnit-BOM的详细信息可以在JUnit官方文档中找到。 JUnit是一个用于编写和运行可重复自动化测试的Java单元测试框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值