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請求體有數據

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