day23【打印流、装饰者模式、commons-io包、网络编程】课上

1. 打印流(了解)

我们之前一直使用输出语句其实就是使用的打印流PrintStream

package com.itheima.sh.printstream_01;

import java.io.PrintStream;

/*
    打印流:PrintStream
    1.打印流只能打印字符数组

 */
public class PrintStreamDemo01 {
    public static void main(String[] args) throws Exception{
        //输出控制台
        /*
            System类的静态属性
                static PrintStream out “标准”输出流。
            PrintStream打印流中的方法:

        */
       /* System.out.println("哈哈");
        PrintStream ps = System.out;
        char[] ch = {'a','您','好'};
        //void print(char[] s)打印字符数组。
        ps.println(ch);//char[] s = {'a','您','好'};

        int[] arr = {10,20,30};
        // void print(Object obj) 打印对象。
        ps.println(arr);//[I@1540e19d  Object obj = {10,20,30};*/

       //PrintStream(String fileName)  fileName表示关联的文件
        PrintStream ps = new PrintStream("1.txt");
        ps.println("哈哈");
        //改变System.out流方向,可以不输出到控制台
        //static void setOut(PrintStream out)重新分配“标准”输出流的方向
        System.setOut(ps);//表示System.out输出的方向被改变了,到ps对象表示的文件1.txt中
        System.out.println("呵呵");
        System.out.println("呵呵");
        System.out.println("呵呵");
        System.out.println("呵呵");
    }
}

小结:

1.PrintStream表示打印字节流,打印流只能打印字符数组

2.装饰设计模式(掌握)

  • 作用:可以在不继承某个类的时候就可以对某个类的方法进行增强(改变)

  • 要求:

    • 被装饰的类和装饰的类必须实现共同父接口,这样才可以保证两个类具有共同方法
    • 装饰的类必须要持有被装饰类的对象
    • 装饰类可以对被装饰类的方法进行增强或者修改
  • 代码演示:

    package com.itheima.sh.decker_02;
    /*
        共同父接口:
            - 被装饰的类和装饰的类必须实现共同父接口,
                    这样才可以保证两个类具有共同方法
            - 装饰的类必须要持有被装饰类的对象
            - 装饰类可以对被装饰类的方法进行增强或者修改
     */
    public interface Star {
        //抽象方法
        void liveShow();
    
        void sing();
    }
    
    
    package com.itheima.sh.decker_02;
    /*
        被装饰类:
     */
    public class LiuDeHua implements Star{
        @Override
        public void liveShow() {
            System.out.println("刘德华参加我是真正男子汉");
        }
    
        @Override
        public void sing() {
            System.out.println("刘德华唱了一首我算什么男人");
        }
    }
    
    
    package com.itheima.sh.decker_02;
    /*
        装饰类:增强类
         装饰类可以对被装饰类的方法进行增强或者修改
     */
    public class BufferedLiuDeHua implements Star{
        //装饰的类必须要持有被装饰类的对象
        //定义构造方法
        LiuDeHua ldh;
        public BufferedLiuDeHua(LiuDeHua ldh) {
            this.ldh = ldh;
        }
    
        @Override
        public void liveShow() {
            //保留被装饰类原来的方法
            ldh.liveShow();
        }
    
        @Override
        public void sing() {
            System.out.println("刘德华唱了一首冰雨");
        }
    }
    
    package com.itheima.sh.decker_02;
    
    public class Test01 {
        public static void main(String[] args) {
            //创建被装饰类对象
            LiuDeHua ldh = new LiuDeHua();
            ldh.liveShow();
            ldh.sing();
    
    
            //创建装饰类对象
            BufferedLiuDeHua bldh = new BufferedLiuDeHua(ldh);
            bldh.liveShow();
            bldh.sing();
    
        }
    }
    
    

    小结:

    1.装饰者设计模式可以在不继承某个类的情况下就可以实现对某个类的方法的增强或者修改

    2.要求:

     - 被装饰的类和装饰的类必须实现共同父接口,这样才可以保证两个类具有共同方法
     - 装饰的类必须要持有被装饰类的对象
     - 装饰类可以对被装饰类的方法进行增强或者修改
    

3.commons-io包(理解)

1.commons-io属于第三方的技术即Apache公司。技术开源的。

2.第一方是oracle 我们是第二方 其余都是第三方

3.以后只要使用第三的技术,jdk是不提供的,我们需要到官网下载第三方包,然后将第三方包导入到idea中并解压才可以使用

4.导包步骤:

1)

在这里插入图片描述

2)需要在当前项目根目录下手动创建一个文件夹叫 lib,将下载好的包放到该文件夹下

在这里插入图片描述

3)解压包

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5.核心类:

​ 1)IOUtils类中

  • public static int copy(InputStream in, OutputStream out) 文件复制(适合文件大小为2GB以下)

  • public static long copyLarge(InputStream in, OutputStream out) 文件复制(适合文件大小为2GB以上)

    参数:

    ​ in:表示关联的数据源文件

    ​ out:表示关联的目的地文件

/*
        IOUtils类中:
         public static int copy(InputStream in, OutputStream out)      文件复制(适合文件大小为2GB以下)

            参数:

        ​	in:表示关联的数据源文件

        ​	out:表示关联的目的地文件
     */
    private static void method_1() throws Exception{
        //in:表示关联的数据源文件
//        FileInputStream fis = new FileInputStream("D:\\test\\4.jpg");
//        //out:表示关联的目的地文件
//        FileOutputStream fos = new FileOutputStream("4.jpg");
//        // 使用IOUtils类中:静态方法快速复制
//        IOUtils.copy(fis, fos);
        IOUtils.copy(new FileInputStream("D:\\test\\4.jpg"), new FileOutputStream("4.jpg"));
//        fos.close();
//        fis.close();

    }

​ 2)FileUtils类中:

  • public static void copyFileToDirectory(File srcFile, File destFile) //复制文件到另外一个目录下。
    • 参数:srcFile表示源文件 destFile 表示目的地的文件夹
  • public static void copyDirectoryToDirectory(File srcDir,File destDir);文件夹复制,将srcDir复制到
    • 参数: srcDir 表示数据源文件夹 destDir :表示目的地文件夹

代码演示:

/*
        FileUtils类:
        需求2:D:\\test文件夹复制到F盘下
        public static void copyDirectoryToDirectory(File srcDir,File destDir);文件夹复制,将srcDir复制到
            - 参数: srcDir 表示数据源文件夹   destDir :表示目的地文件夹
 */
    private static void method_3()throws Exception {
        FileUtils.copyDirectoryToDirectory(new File("D:\\test"),new File("F:\\"));
    }

    /*
        FileUtils类:
        需求1:将D:\1.mp3复制到F盘
        public static void copyFileToDirectory(File srcFile, File destFile) //复制文件到另外一个目录下。
            - 参数:srcFile表示源文件   destFile 表示目的地的文件夹
     */
    private static void method_2() throws Exception{
        FileUtils.copyFileToDirectory(new File("D:\\1.mp3"),new File("F:\\"));
    }

4.网络编程介绍(了解)

我们之前学习的数据最开始是在内存中,后来学习IO流可以将数据长久保存本地硬盘上,如果我们想将数据通过网络传输到其他人电脑上,就可以使用网络编程技术。

我们想学习网络编程必须先学习网络编程的一些规范。

5.网络三要素(重点)

举例:我请柳岩吃饭。

1)住址:上海航头镇 航都路18号 ip地址

2)详细地址:5号楼501房间 端口号

3)沟通方式:使用汉语 协议

5.1ip地址

类似于上述举例的住址。

在网络编程中通过计算机的唯一标识ip地址找到目标计算机。在同一个网络中,ip地址是唯一的。

在计算机中如何查看ip地址呢?

在dos窗口中输入命令:

ipconfig
ipv4:192.168.91.1
IPv6 地址. . . . . . . . : fe80::689f:b50d:b445:9bff%16

ipv4: Internet protocal version 4 互联网协议(规范)版本4

ipv4版本的ip地址采用点分十进制表示的

192.       168.         91.          1

11000000   10101000    01011011     00000001
4*8--32--->表示的ip地址个数:232次方大概是43亿左右 

IPv6 : 8*16—》128

特殊ip地址:

本地回环ip地址就是本机ip地址:

127.0.0.1  

如何查看当前电脑和其他计算机是否可以进行网路传输:

ping  目标计算机的ip地址或者域名
ping www.baidu.com

5.2.端口号

相当于上述举例中的:详细地址:5号楼501房间。

1.一台计算机中具有多个进程。每个进程都会有唯一的标识即端口号。

2.端口号是的范围是:0-65535 系统进程的端口号范围:0-1024 所以建议我们自己定义的端口号尽量不要小于1024,有可能会引发冲突。导致进程访问出现问题。

在这里插入图片描述

小结:

​ 通过ip地址找到目标计算机,通过端口号找到目标计算机中的具体的进程。

5.3网络协议

相当于上述举例的 沟通方式:使用汉语。

属于网络编程中的规范。必须满足规范。

TCP/IP协议(Transmission Control Protocal/Internet Protocal传输控制协议/英特网互联协议

网络分层:

在这里插入图片描述

TCP协议特点:

​ 1.具有服务器端和客户端之分的

​ 2.必须先有服务器端,再有客户端

​ 3.传输数据是安全的,数据不会丢失的,满足三次握手协议

在这里插入图片描述

4.但是效率低

UDP:用户数据报包协议。UDP 是User Datagram Protocol的简称, 中文名是用户数据报包协议。

特点:

​ 1.不一定有服务器端,如果只有客户端可以发送数据

​ 2.数据不安全

​ 3.但是效率高

​ 4.最大是64K

类似于对讲机。

5.4网络三要素总结

ip地址找目标计算机

端口号找目标计算机中的进程

协议:是规范 TCP/HTTP/UDP

6.InetAddress类(掌握)

1.该类表示ip地址的。

2.获取该类对象:使用InetAddress类的静态方法:

static InetAddress getByName(String host) 给定主机名的情况下确定主机的 IP 地址。 
    	参数:host可以是主机名,也可以是域名或者ip地址

3.方法:

String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。 
String getHostName() 获取此 IP 地址的主机名 

4.代码演示:

package com.itheima.sh.inetaddress_04;

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

/*
    1.static InetAddress getByName(String host) 给定主机名的情况下确定主机的 IP 地址。
    	参数:host可以是主机名,也可以是域名或者ip地址

   2.方法:
    String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
    String getHostName() 获取此 IP 地址的主机名
 */
public class InetAddressDemo01 {
    public static void main(String[] args) throws UnknownHostException {
        //1.static InetAddress getByName(String host) 给定主机名的情况下确定主机的 IP 地址。
        //    	参数:host可以是主机名,也可以是域名或者ip地址

//        InetAddress ia = InetAddress.getByName("suoge");//    10.254.77.1  suoge
//        InetAddress ia = InetAddress.getByName("127.0.0.1");// 127.0.0.1  127.0.0.1
        InetAddress ia = InetAddress.getByName("www.baidu.com");//  112.80.248.76   www.baidu.com
        //使用对象调用方法:
        String hostAddress = ia.getHostAddress();//获取ip地址的
        System.out.println("hostAddress = " + hostAddress);

        //获取主机名  String getHostName() 获取此 IP 地址的主机名
        String hostName = ia.getHostName();
        System.out.println("hostName = " + hostName);//suoge

    }
}

小结:

1.InetAddress类表示计算机的ip地址

2.获取类的对象方法:

static InetAddress getByName(String host) 给定主机名的情况下确定主机的 IP 地址。
    	参数:host可以是主机名,也可以是域名或者ip地址

3.方法:

    String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
    String getHostName() 获取此 IP 地址的主机名

7.TCP通信程序(掌握)

特点:

1.有客户端和服务器端,必须先有服务器端,在使用客户端连接服务器

2.满足三次握手协议,安全,效率低

3.传输的数据没有大小限制

7.1客户端Socket类

使用的类是Socket类。

1.构造方法

Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
    参数:
    	host:表示连接的服务器的ip地址或者域名或者主机名
        port:服务器的端口号

2.获取向连接通道中写数据的字节输出流

 OutputStream getOutputStream() 返回此套接字的输出流。 

3.代码演示:

package com.itheima.sh.tcp_05;

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

/*
    客户端:
    必须先有服务器端,否则就会报异常:Exception in thread "main" java.net.ConnectException: Connection refused: connect
 */
public class ClientDemo01 {
    public static void main(String[] args) throws Exception{
        //1.创建客户端对象
        /*
            Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
            参数:
                host:表示连接的服务器的ip地址或者域名或者主机名  127.0.0.1 
                port:服务器的端口号 8888
         */
        Socket s = new Socket("127.0.0.1", 8888);
        //2.获取向连接通道中写数据的字节输出流对象
        // OutputStream getOutputStream() 返回此套接字的输出流。
        OutputStream os = s.getOutputStream();
        //3.使用字节输出流向通道中写数据
        os.write("hello,TCP我来了!".getBytes());
        //关闭资源
        os.close();
        s.close();
    }
}

小结:

  1. Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。

    参数:
    host:表示连接的服务器的ip地址或者域名或者主机名 127.0.0.1
    port:服务器的端口号 8888

  2. OutputStream getOutputStream() 返回此套接字的输出流。

7.2服务器端ServerSocket类

1.此类实现服务器套接字

2.构造方法:

ServerSocket(int port) 创建绑定到特定端口的服务器套接字
    	参数:port表示服务器绑定到端口号,然后客户端根据该端口号连接服务器

3.使用服务器的套接字对象调用方法获取客户端

Socket accept() 侦听并接受到此套接字的连接 

4.使用获取的客户端套接字对象调用方法获取从通道中读取数据的字节输入流对象

InputStream getInputStream() 返回此套接字的输入流。 

5.代码演示:

package com.itheima.sh.tcp_05;

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

/*
    服务器端:ServerSocket:
    1.创建服务器的对象:
        ServerSocket(int port) 创建绑定到特定端口的服务器套接字
    	参数:port表示服务器绑定到端口号,然后客户端根据该端口号连接服务器
   2.使用服务器的套接字对象调用方法获取客户端
        Socket accept() 侦听并接受到此套接字的连接
   3.使用获取的客户端套接字对象调用方法获取从通道中读取数据的字节输入流对象
        InputStream getInputStream() 返回此套接字的输入流。

 */
public class ServerDemo02 {
    public static void main(String[] args) throws Exception{
        //1.创建服务器的对象:
        ServerSocket ss = new ServerSocket(8888);
        //2.使用服务器的套接字对象调用方法获取客户端  Socket accept() 侦听并接受到此套接字的连接
        Socket s = ss.accept();
        //3.使用获取的客户端套接字对象调用方法获取从通道中读取数据的字节输入流对象
        //  InputStream getInputStream() 返回此套接字的输入流。
        InputStream is = s.getInputStream();
        //4.读取客户端发送的数据
        byte[] buf = new byte[1024];
        int len;
        while((len=is.read(buf))!=-1){
            //输出客户端发送的内容
            System.out.println(new String(buf,0,len));
        }
        //5.关闭资源
        is.close();
        s.close();
        ss.close();//在实际开发中服务器不要关闭
    }
}

小结:

​ 1.创建服务器的对象:
​ ServerSocket(int port) 创建绑定到特定端口的服务器套接字
​ 参数:port表示服务器绑定到端口号,然后客户端根据该端口号连接服务器
2.使用服务器的套接字对象调用方法获取客户端
​ Socket accept() 侦听并接受到此套接字的连接
​ 3.使用获取的客户端套接字对象调用方法获取从通道中读取数据的字节输入流对象
​ InputStream getInputStream() 返回此套接字的输入流。

图解:

在这里插入图片描述

8.综合案例(必须完成)

8.1文件上传案例(带反馈信息的文件的上传实现)

需求:将客户端的文本文件上传到服务器端的硬盘上.

1.入门

package com.itheima.sh.tcp_test_06;

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

/*
    客户端:
    步骤:
    1.创建字符输入缓冲流关联客户端的数据源文件 D:\test\故事.txt
    2.创建客户端套接字(Socket)对象指定服务器的ip地址和端口号
    3.根据客户端套接字对象获取向通道中写数据的字节输出流关联通道
    4.将上述的字节输出流转换为字符输出缓冲流
    5.使用字符输入缓冲流读取数据源文件到客户端内存中
    6.使用字符输出缓冲流将内存中的数据写到通道中
    7.关闭资源
 */
public class ClientTest01 {
    public static void main(String[] args) throws Exception{
        //1.创建字符输入缓冲流关联客户端的数据源文件 D:\test\故事.txt
        BufferedReader br = new BufferedReader(new FileReader("D:\\test\\故事.txt"));
        //2.创建客户端套接字(Socket)对象指定服务器的ip地址和端口号
        Socket s = new Socket("127.0.0.1", 9999);
        //3.根据客户端套接字对象获取向通道中写数据的字节输出流关联通道
        OutputStream os = s.getOutputStream();
        //4.将上述的字节输出流转换为字符输出缓冲流:使用输出转换流
        //OutputStreamWriter(OutputStream out)
        //OutputStreamWriter父类是Writer
        OutputStreamWriter osw = new OutputStreamWriter(os);
        //创建字符输出缓冲流对象 BufferedWriter(Writer out)
        BufferedWriter bw = new BufferedWriter(osw);
        //5.使用字符输入缓冲流读取数据源文件到客户端内存中
        String line;
        while((line=br.readLine())!=null){
            //6.使用字符输出缓冲流将内存中的数据写到通道中
            bw.write(line);
            //换行
            bw.newLine();
            //刷新
            bw.flush();
        }
        //7.关闭资源
        bw.close();
        osw.close();
        os.close();
        s.close();
        br.close();
    }
}


package com.itheima.sh.tcp_test_06;

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

/*
    服务器:
    步骤:
    1.创建服务器端的套接字对象
    2.侦听并获取客户端套接字
    3.使用客户端套接字对象调用方法获取从通道中读取数据的字节输入流
    4.将字节输入流转换为字符输入缓冲流BufferedReader
    5.创建字符输出缓冲流对象关联服务器端的硬盘文件
    6.使用字符输入缓冲流读取通道的数据到服务器内存中
    7.使用字符输出缓冲流将内存中的数据写到服务器硬盘文件中
    8.关闭资源
 */
public class ServerTest02 {
    public static void main(String[] args) throws Exception{
        //1.创建服务器端的套接字对象
        ServerSocket ss = new ServerSocket(9999);
        //2.侦听并获取客户端套接字
        Socket s = ss.accept();
        //3.使用客户端套接字对象调用方法获取从通道中读取数据的字节输入流
        InputStream is = s.getInputStream();
        //4.将字节输入流转换为字符输入缓冲流BufferedReader
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        //5.创建字符输出缓冲流对象关联服务器端的硬盘文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("故事.txt"));
        //6.使用字符输入缓冲流读取通道的数据到服务器内存中
        String line;
        while((line=br.readLine())!=null){
            // 7.使用字符输出缓冲流将内存中的数据写到服务器硬盘文件中
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        //8.关闭资源
        bw.close();
        br.close();
        is.close();
        s.close();
        ss.close();
    }
}

在这里插入图片描述

2.带反馈信息

package com.itheima.sh.tcp_test_07;

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

/*
    客户端:
    步骤:
    1.创建字符输入缓冲流关联客户端的数据源文件 D:\test\故事.txt
    2.创建客户端套接字(Socket)对象指定服务器的ip地址和端口号
    3.根据客户端套接字对象获取向通道中写数据的字节输出流关联通道
    4.将上述的字节输出流转换为字符输出缓冲流
    5.使用字符输入缓冲流读取数据源文件到客户端内存中
    6.使用字符输出缓冲流将内存中的数据写到通道中
    7.关闭资源
 */
public class ClientTest01 {
    public static void main(String[] args) throws Exception{
        //1.创建字符输入缓冲流关联客户端的数据源文件 D:\test\故事.txt
        BufferedReader br = new BufferedReader(new FileReader("D:\\test\\故事.txt"));
        //2.创建客户端套接字(Socket)对象指定服务器的ip地址和端口号
        Socket s = new Socket("127.0.0.1", 9999);
        //3.根据客户端套接字对象获取向通道中写数据的字节输出流关联通道
        OutputStream os = s.getOutputStream();
        //4.将上述的字节输出流转换为字符输出缓冲流:使用输出转换流
        //OutputStreamWriter(OutputStream out)
        //OutputStreamWriter父类是Writer
        OutputStreamWriter osw = new OutputStreamWriter(os);
        //创建字符输出缓冲流对象 BufferedWriter(Writer out)
        BufferedWriter bw = new BufferedWriter(osw);
        //5.使用字符输入缓冲流读取数据源文件到客户端内存中
        String line;
        while((line=br.readLine())!=null){
            //6.使用字符输出缓冲流将内存中的数据写到通道中
            bw.write(line);
            //换行
            bw.newLine();
            //刷新
            bw.flush();
        }

        System.out.println("客户端.....");
        //给服务器写结束标记
       /* bw.write("over");
        //换行
        bw.newLine();
        //刷新
        bw.flush();*/
        /*
            使用Socket类中的方法写结束标记
             void shutdownOutput() 禁用此套接字的输出流。
         */
        s.shutdownOutput();
        //接收服务器的反馈信息
        //获取字节输入流对象关联通道
        InputStream is = s.getInputStream();
        byte[] buf = new byte[1024];
        /*
            客户端阻塞到read方法这里,我们到现在为止学习io流都具有阻塞功能
            称为BIO(Blocking 阻塞的IO)
            作为客户端当我们上传完文件之后jvm继续向下执行,当执行到read方法这里
            jvm阻塞,等待服务器端给反馈信息
         */
        int len = is.read(buf);
        System.out.println("222222");
        System.out.println(new String(buf,0,len));


        //7.关闭资源
        bw.close();
        osw.close();
        os.close();
        s.close();
        br.close();
    }
}



package com.itheima.sh.tcp_test_07;

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

/*
    服务器:
    步骤:
    1.创建服务器端的套接字对象
    2.侦听并获取客户端套接字
    3.使用客户端套接字对象调用方法获取从通道中读取数据的字节输入流
    4.将字节输入流转换为字符输入缓冲流BufferedReader
    5.创建字符输出缓冲流对象关联服务器端的硬盘文件
    6.使用字符输入缓冲流读取通道的数据到服务器内存中
    7.使用字符输出缓冲流将内存中的数据写到服务器硬盘文件中
    8.关闭资源
 */
public class ServerTest02 {
    public static void main(String[] args) throws Exception{
        //1.创建服务器端的套接字对象
        ServerSocket ss = new ServerSocket(9999);
        //2.侦听并获取客户端套接字
        Socket s = ss.accept();
        //3.使用客户端套接字对象调用方法获取从通道中读取数据的字节输入流
        InputStream is = s.getInputStream();
        //4.将字节输入流转换为字符输入缓冲流BufferedReader
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        //5.创建字符输出缓冲流对象关联服务器端的硬盘文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("故事.txt"));
        //6.使用字符输入缓冲流读取通道的数据到服务器内存中
        String line;
        /*
            服务器端阻塞在while循环这里,因为br是关联通道的,不是本地文件
            对于服务器这里,并不知道读取文件已经结束,服务器还会继续停留在这里等待客户端
            向通道中写数据。
            其实就是让服务器直到客户端已经上传完毕,然后服务器需要结束while循环
            让服务器向下运行
         */
        while((line=br.readLine())!=null){
            // 7.使用字符输出缓冲流将内存中的数据写到服务器硬盘文件中
            //判断是否结束
            /*if("over".equals(line)){
                //说明结束
                break;
            }*/
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        System.out.println("服务器端......");

        //给客户端写反馈信息 关联通道
        OutputStream os = s.getOutputStream();
        os.write("种子已经上传成功".getBytes());

        //8.关闭资源
        os.close();
        bw.close();
        br.close();
        is.close();
        s.close();
        ss.close();
    }
}

小结:

1.之前学习的IO属于阻塞式的称为BIO

2.向通道中书写结束标记:

使用Socket类中的方法写结束标记: void shutdownOutput() 禁用此套接字的输出流。

3.文件上传优化服务器分析

1.解决多个客户端上传文件同名的问题

//我们可以将文件名进行每次都重新命名
        BufferedWriter bw = new BufferedWriter(new FileWriter(System.currentTimeMillis()+".txt"));

2.解决客户端上传成功之后,服务器一直运行

 while(true){
            //2.侦听并获取客户端套接字
            Socket s = ss.accept();
            //3.使用客户端套接字对象调用方法获取从通道中读取数据的字节输入流
            InputStream is = s.getInputStream();
            //4.将字节输入流转换为字符输入缓冲流BufferedReader
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //5.创建字符输出缓冲流对象关联服务器端的硬盘文件
            //我们可以将文件名进行每次都重新命名
            BufferedWriter bw = new BufferedWriter(new FileWriter(System.currentTimeMillis()+".txt"));
            //6.使用字符输入缓冲流读取通道的数据到服务器内存中
            String line;
        /*
            服务器端阻塞在while循环这里,因为br是关联通道的,不是本地文件
            对于服务器这里,并不知道读取文件已经结束,服务器还会继续停留在这里等待客户端
            向通道中写数据。
            其实就是让服务器直到客户端已经上传完毕,然后服务器需要结束while循环
            让服务器向下运行
         */
            while((line=br.readLine())!=null){
                // 7.使用字符输出缓冲流将内存中的数据写到服务器硬盘文件中
                //判断是否结束
            /*if("over".equals(line)){
                //说明结束
                break;
            }*/
                bw.write(line);
                bw.newLine();
                bw.flush();
            }

            System.out.println("服务器端......");

            //给客户端写反馈信息 关联通道
            OutputStream os = s.getOutputStream();
            os.write("种子已经上传成功".getBytes());

            //8.关闭资源
            os.close();
            bw.close();
            br.close();
            is.close();
            s.close();
//            ss.close();
        }

说明:

1.为了模拟服务器一直运行,我们这里使用死循环 只有一个服务器,创建服务器的代码:

ServerSocket ss = new ServerSocket(9999);

2.服务器别关闭

3.使用多线程解决上传大文件时每个客户端都具有自己的线程进行上传文件

package com.itheima.sh.tcp_test_07;

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

/*
    服务器:
    步骤:
    1.创建服务器端的套接字对象
    2.侦听并获取客户端套接字
    3.使用客户端套接字对象调用方法获取从通道中读取数据的字节输入流
    4.将字节输入流转换为字符输入缓冲流BufferedReader
    5.创建字符输出缓冲流对象关联服务器端的硬盘文件
    6.使用字符输入缓冲流读取通道的数据到服务器内存中
    7.使用字符输出缓冲流将内存中的数据写到服务器硬盘文件中
    8.关闭资源
 */
public class ServerTest02 {
    public static void main(String[] args) throws Exception {
        //1.创建服务器端的套接字对象
        ServerSocket ss = new ServerSocket(9999);
        //为了模拟服务器一直运行,我们这里使用死循环 只有一个服务器
        while (true) {
            //2.侦听并获取客户端套接字
            Socket s = ss.accept();

            //为每个客户端开启线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //3.使用客户端套接字对象调用方法获取从通道中读取数据的字节输入流
                        InputStream is = s.getInputStream();
                        //4.将字节输入流转换为字符输入缓冲流BufferedReader
                        BufferedReader br = new BufferedReader(new InputStreamReader(is));
                        //5.创建字符输出缓冲流对象关联服务器端的硬盘文件
                        //我们可以将文件名进行每次都重新命名
                        BufferedWriter bw = new BufferedWriter(new FileWriter(System.currentTimeMillis() + ".txt"));
                        //6.使用字符输入缓冲流读取通道的数据到服务器内存中
                        String line;
                        /*
                            服务器端阻塞在while循环这里,因为br是关联通道的,不是本地文件
                            对于服务器这里,并不知道读取文件已经结束,服务器还会继续停留在这里等待客户端
                            向通道中写数据。
                            其实就是让服务器直到客户端已经上传完毕,然后服务器需要结束while循环
                            让服务器向下运行
                         */
                        while ((line = br.readLine()) != null) {
                            // 7.使用字符输出缓冲流将内存中的数据写到服务器硬盘文件中
                            //判断是否结束
                            /*if("over".equals(line)){
                                //说明结束
                                break;
                            }*/
                            bw.write(line);
                            bw.newLine();
                            bw.flush();
                        }
                        System.out.println("服务器端......");
                        //给客户端写反馈信息 关联通道
                        OutputStream os = s.getOutputStream();
                        os.write("种子已经上传成功".getBytes());
                        //8.关闭资源
//                        os.close();
//                        bw.close();
//                        br.close();
//                        is.close();
//                        s.close();
                    } catch (Exception e) {

                    }
                }
            }).start();

        }

    }
}

在这里插入图片描述

8.2模拟服务器(扩展)

package com.itheima.sh.tcp_test_08;

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

/*
    服务器端:
    需求:自己手动编写服务器。使用浏览器访问我们自己书写的服务器
    浏览器地址栏访问的路径:http://服务器ip地址:服务器端口号/资源路径
 */
public class ServerTest01 {
    public static void main(String[] args) throws Exception{
        //1.创建ServerSocket类的对象
        ServerSocket ss = new ServerSocket(12306);
        //2.侦听并获取客户端套接字
        Socket s = ss.accept();
        //3.获取从通道中读取数据的字节输入流
        InputStream is = s.getInputStream();
        byte[] buf = new byte[1024*100];
        int len = is.read(buf);
        System.out.println(new String(buf,0,len));
        is.close();
        s.close();
    }
}

说明:

浏览器向服务器发送的请求信息:

GET /web/index.html HTTP/1.1
Host: 127.0.0.1:12306
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,und;q=0.7

请求信息包含:

1)请求行

GET /web/index.html HTTP/1.1

a:请求方式:在开发中有八种,常用的有两种:GET POST

​ GET : 将请求的数据放到请求路径的后面

http://127.0.0.1:12306/web/index.html?username=锁哥&password=1234
不安全,可以作为商品分享。
    
https://login.taobao.com/member/login.jhtml?redirectURL=http%3A%2F%2Fs.taobao.com%2Flist%3Fspm%3Da217m.8316598.313651-static.12.6dfa33d5do5AVQ%26q%3D%25E5%258D%25AB%25E8%25A1%25A3%26cat%3D50344007%26style%3Dgrid%26seller_type%3Dtaobao&uuid=e8799c3ff0aa12a0f85070f8f5294474

请求体没有数据

​ POST请求:安全:

http://127.0.0.1:12306/web/index.html
数据放到请求体中

​ b:请求资源路径 : /web/index.html

​ c:请求协议/版本: HTTP/1.1

2)请求头

Host: 127.0.0.1:12306
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,und;q=0.7

key:value形式 关于客户端浏览器信息 系统信息

3)请求体

get请求体没有数据

post请求体有数据

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章