Java網(wǎng)絡 Day03 2020-04-27

內(nèi)容

1.HTML??和Apache服務器后臺程序(PHP)如何進行交互
2.OSI七層模型(了解)
3.HTTP與TCP
4.請求(Request)和響應(Response)
5.三次握手建立連接
6.IP地址與DNS
7.端口號
8.URL、URLEncoder和URLDecoder
9.URL類的簡單使用
10.URL類下載圖片/視頻
11.多線程下載器項目(難點)

一.HTML??和Apache服務器后臺程序(PHP)如何進行交互

①我們在瀏覽器中輸???的地址,比如127.0.0.1/login.html,進入網(wǎng)頁
②??中可以向服務器提交數(shù)據(jù)form表單
③服務器端使?對應的?式接收表單中的數(shù)據(jù),比如 _GET["name"] 或_POST["name"]
④服務器使?echo返回給??數(shù)據(jù)

二.OSI七層模型(了解)

1.應用層

網(wǎng)絡服務與最終用戶的一個接口
協(xié)議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

2. 表示層

數(shù)據(jù)的表示、安全、壓縮。(在五層模型里面已經(jīng)合并到了應用層
格式有,JPEG、ASCll、EBCDIC、加密格式等

3. 會話層

建立、管理、終止會話。(在五層模型里面已經(jīng)合并到了應用層
對應主機進程,指本地主機與遠程主機正在進行的會話

4. 傳輸層

定義傳輸數(shù)據(jù)的協(xié)議端口號,以及流控和差錯校驗。
協(xié)議有:TCP UDP,數(shù)據(jù)包一旦離開網(wǎng)卡即進入網(wǎng)絡傳輸層

5. 網(wǎng)絡層

進行邏輯地址尋址,實現(xiàn)不同網(wǎng)絡之間的路徑選擇。
協(xié)議有:ICMP IGMP IP(IPV4 IPV6)

6. 數(shù)據(jù)鏈路層

建立邏輯連接、進行硬件地址尋址、差錯校驗 等功能。(由底層網(wǎng)絡定義協(xié)議)
將比特組合成字節(jié)進而組合成幀,用MAC地址訪問介質(zhì),錯誤發(fā)現(xiàn)但不能糾正。

7. 物理層

建立、維護、斷開物理連接。(由底層網(wǎng)絡定義協(xié)議)

三.HTTP與TCP

1.HTTP協(xié)議:

Hyper Text Transfer Protocol,即超文本傳輸協(xié)議,是一個基于請求與響應模式、無狀態(tài)的網(wǎng)絡協(xié)議,是瀏覽器和服務器間最常用的通訊協(xié)議。
HTTP協(xié)議包括兩部分,請求協(xié)議和響應協(xié)議

2.URL與URI:

URLUniform Resource Locator,即統(tǒng)一資源定位符(可以理解成一個網(wǎng)址就是一個URL,但是URL不一定就是網(wǎng)址),是互聯(lián)網(wǎng)上標準資源的地址,可以在全球范圍內(nèi)唯一確定的一個資源。
URIUniform Resource Identifier,即統(tǒng)一資源標識符,用于標識一個資源的名稱。通過這種名稱命名的資源可以被互聯(lián)網(wǎng)定位和訪問

3.HTTP和TCP關系

客戶端若要向服務器端發(fā)出請求,必須首先在它們之間建立一個TCP(Transfer Control Protocal,即傳輸控制協(xié)議)連接,當客戶端與服務器端的通訊結束后,TCP連接將被關閉,而這個連接就是基于HTTP協(xié)議的
②HTTP1.0在客戶端接收到服務器端發(fā)送來的響應后,TCP馬上關閉,而HTTP1.1不同,客戶端在發(fā)送創(chuàng)建TCP連接請求之前首先計算出本次連接中,瀏覽器所要發(fā)送的請求數(shù)量,即一次手工請求加上其所攜帶的所有自動請求數(shù)量,當所有瀏覽器所發(fā)出的請求全部發(fā)送完畢后,客戶端會自動發(fā)送一個關閉TCP連接的請求,這個請求在HTTPWatch中是看不到的。為了防止服務器主動將TCP連接關閉,在每一個請求中都攜帶了一個參數(shù)Connection,用于告訴服務器是否關閉連接,在HTTPWatch中可以看到的這些請求中,其Connection參數(shù)值均為Keep-Alive保持連接,只有當客戶端發(fā)送了關閉TCP連接請求(Connection為close)時,服務器才會將TCP連接關閉

四.請求(Request)和響應(Response)

1.注意:

請求和響應是成對出現(xiàn)的

2.請求(Request):(一般是瀏覽器發(fā)出)

(1)何時發(fā)出請求?

一般情況下,當
①客戶端需要向服務器 上傳數(shù)據(jù)
②客戶端需要從服務器 下載數(shù)據(jù)
時,瀏覽器就會向服務器端發(fā)出請求,在這些請求中,有用戶手動提出的請求,也有瀏覽器自動發(fā)出的請求

(2)常用的請求方式:GET和POST
(3)GET請求

由于GET請求會將請求所攜帶的參數(shù)作為URL中的一部分出現(xiàn),所以請求參數(shù)會顯示在地址欄,這就導致了GET提交的三點不足
①參數(shù)值只能是字符串,而不能是其他類型
②可以攜帶的數(shù)據(jù)量小
③數(shù)據(jù)安全性低

但GET請求有一個很重要的特征:客戶端一旦接收到“服務器向GET請求發(fā)送的響應”后,瀏覽器會自動緩存響應,當客戶再次進行相同請求的提交時,將直接讀取本地瀏覽器緩存中的數(shù)據(jù),而不再向服務器端真正發(fā)送數(shù)據(jù),讓用戶感覺服務器端的響應很快提升用戶體驗,減輕了服務器壓力

(4)POST請求

POST請求會將請求所攜帶的數(shù)據(jù)以請求正文的形式出現(xiàn)(而不是在URL中),所以與GET相比,就顯示出了幾點長處:
①數(shù)據(jù)類型可以是任意類型,還可以是聲音、視頻、圖片等文件
②請求可以攜帶的數(shù)據(jù)量大(理論上沒有上限)
③數(shù)據(jù)安全性高

但發(fā)出POST請求的客戶端瀏覽器不會對接收到的“服務器向POST請求發(fā)送的響應”進行緩存,所以當用戶再次進行相同請求時,仍是真正向服務器發(fā)送的請求,從服務器讀取的數(shù)據(jù)

(5)為何GET和POST不都有緩存?

主要原因是:以不同的方式提交請求,其目的也是不同的
Ⅰ:GET(意為得到)請求的目的一般是客戶端要從服務器端下載資源,發(fā)送相同的請求就代表要下載相同的資源。既然要下載相同的資源,而這些資源已經(jīng)被下載到了客戶端,那么就無需再下載了,所以也就無需再向服務器發(fā)送真正的下載請求了。所以就將GET提交方式設計為了“GET請求的響應結果會被瀏覽器緩存”。
Ⅱ:但POST(意為上傳)請求的目的一般是客戶端要向服務器端上傳資源,對于向服務器端上傳資源后的響應結果,瀏覽器是無需緩存的(緩存了沒有意義

(6)GET和POST如何進行選擇

①當提交時所攜帶的數(shù)據(jù)類型不是字符串(比如文件、文本、音頻、對象等等)時
②提交時所攜帶的數(shù)據(jù)量比較大
③提交時所攜帶的數(shù)據(jù)具有敏感性,安全性要求較高
都用POST其余情況均用GET

3.響應Response:

響應即服務器端對客戶端的請求作出的回應

4.抓包工具:

如果想要查看地址欄所發(fā)出的請求詳情,那么我們可以通過抓包工具來攔截HTTP請求與響應,從工具中可以看到具體的請求與響應內(nèi)容,抓包工具很多,最常用的是HttpWatch

5.HEAD

HEAD:只是獲取服務器端返回的響應信息 ,不會獲取具體的內(nèi)容

6.狀態(tài)碼:

①2xx:表示對請求計算與響應成功,其中常用的狀態(tài)碼是200
②4xx:表示請求錯誤。其中常見的狀態(tài)碼是404,表示資源找不到,一般都是請求路徑書寫有問題
③5xx:表示服務器端錯誤,其中常見的狀態(tài)碼是500,表示服務器內(nèi)部有錯誤,一般都是服務器端的Java代碼發(fā)生錯誤
具體例子
200-206 請求成功
300-305 重定向 比如 www.baidu.com 然后跳轉到 www.qq.com
400-415 客戶端錯誤
500-505 服務器端錯誤

五.三次握?建?連接

HTTP封裝完數(shù)據(jù)之后,需要將數(shù)據(jù)使?TCP協(xié)議向?絡中的其他主機(服務器)進?發(fā)送
需要經(jīng)過三個過程/三次握?
可以概括為:①先問問有沒有(發(fā)送請求)(類似打電話開始播),②然后服務器告訴你有沒有(進行數(shù)據(jù)校驗)(類似打電話時提示 撥打的電話不存在或噔噔的聲音)③最后獲取數(shù)據(jù)(建立連接)(類似打電話的說話)

六.IP地址與DNS

①IP地址 就是?來唯?標識?絡中的?臺設備,比如127.0.0.1
②域名就類似:www.baidu.com域名和IP地址是一一對應的關系,因為IP地址不好記,所以有了域名。
③DNS :域名解析器,輸入域名之后系統(tǒng)如何識別?就是用DNS將域名解析成IP地址。
比如:
http://127.0.0.1/login.php?user_name=jack&user_password=123
①http: 數(shù)據(jù)傳輸使?的具體協(xié)議(除了http,還有https)
②127.0.0.1或者 www.baidu.com :訪問的主機地址
③login.php: 訪問主機的那個?件或者?錄(也就是具體的服務)
④?:分隔符 表示:需要向服務器提交數(shù)據(jù) 。提交?式為:GET, 后?就是具體提交的數(shù)據(jù)
⑤user_name=jack就是提交的?個數(shù)據(jù) 。user_name是客戶端(html)定義的字段 。jack是字段對應的數(shù)據(jù)
⑥使?&來連接多個字段

七.端?號

一個端?對應的是?種服務,對應一個應用程序,比如QQ。
一般來說,80端?對應的是?絡服務
公認端?:0-1023 ,用來進行?些特定的服務
注冊端?:1024-49151 自己寫的應?程序要使?該范圍端?
動態(tài)私有端?:49152- 65535

八.URL、URLEncoder和URLDecoder

java使?URL類來封裝?絡數(shù)據(jù)的地址
注意:url地址??不能包含中?或者其他?些特殊的字符, 對于這些字符需要進?編碼或者解碼。

九.URL類的簡單使用

1.常用方法介紹

①getHost():是獲取主機。比如輸出得到127.0.0.1
②getProtocol():是得到協(xié)議,一般是http協(xié)議
③getQuery():是獲取傳遞的參數(shù),比如user_name=jack&user_password=jcnsjacns這些
④getPath():是得到哪個具體的文件,打印就輸出比如 test.php
⑤URLDecoder.decode()和URLEncoder.encode()分別是解碼和編碼

            String word = URLDecoder.decode("%E7%99%BB%E5%BD%95","utf-8");//使用utf-8進行編碼
            String code = URLEncoder.encode("登錄","utf-8");//使用utf-8進行解碼
            System.out.println(word);
            System.out.println(code);

后面兩個分別輸出
登錄
%E7%99%BB%E5%BD%95
使用URLConnection獲取網(wǎng)絡數(shù)據(jù)

2.使用示例(向網(wǎng)絡中發(fā)起氫請求

import java.io.*;
import java.net.MalformedURLException;
import java.net.*;
import java.util.*;

public class 測試程序{                                                                                                             
            

       
        public static void main(String[] args) throws Exception{
           //向網(wǎng)絡中發(fā)起請求
            //1.獲取對應的url地址
            URL url = new URL("http://127.0.0.1:8081/test.php?user_name=jack&user_password=123");//&submit=%E7%99%BB%E5%BD%95

            //2.使用URLConnection(抽象類)發(fā)起鏈接(會做三次握手)
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            //設置請求方式
            conn.setRequestMethod("GET");//默認GET
            conn.setDoInput(true);//設置是否打開接收的流(默認打開)
            conn.setDoOutput(true);//設置是否打開輸出流 POST方式必須要打開
            conn.setConnectTimeout(5*1000);//請求時間,如果5s還沒得到響應信息,那么鏈接斷開
            conn.setUseCaches(false);//設置是否緩存(如果緩存,則下一次打開的時候會塊很多)
            
            //3.接收數(shù)據(jù)(發(fā)送和接收都是用輸入輸出流)
            //openStream 是從服務器端獲取數(shù)據(jù)
            //conn.getOutputStream() 向服務器端發(fā)出數(shù)據(jù)
            InputStream is = url.openStream();
            
            //4.處理輸入流的數(shù)據(jù) 二進制(放到byte數(shù)組里面)然后轉換成String
            byte[] buffer = new byte[1024];
            int len = 0;
            while((len = is.read(buffer)) != -1) {
                String content = new String(buffer,0,len);
                System.out.println(content);
            }
            
            //5.關閉流
            is.close();
            
            
        }
}

十.URL類下載圖片/視頻

注意這里的輸入輸出流怎么寫的。

import java.io.*;
import java.net.MalformedURLException;
import java.net.*;
import java.util.*;

public class 測試程序{                                                                                                             
            

       
        public static void main(String[] args) throws Exception{
         //下載圖片
            //1.url
            URL url = new URL("http://127.0.0.1:8081/uploadtimg.jpg");
            
            //2.連接
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            
            //3.獲取輸入流
            InputStream inputStream = url.openStream();
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            
            //4.創(chuàng)建輸出流
            FileOutputStream fos = new FileOutputStream("C:\\Users\\劉金豪\\Desktop\\test.jpg");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            
            //5.將數(shù)據(jù)寫入磁盤
            byte[] buffer = new byte[1024];
            int len = 0;
            while((len = bis.read(buffer)) != -1) {
                bos.write(buffer,0,len);
            }
            
            //6.關閉流
            bos.close();
            fos.close();
            bis.close();
            inputStream.close();
        }
}


十一.多線程下載器項目

1.總體思路

先知道文件大小,然后分配線程,在此之前要有一個文件來接收數(shù)據(jù)(使用RandomAccessFile的seek方法寫入數(shù)據(jù))
多線程下載獲取文件大小

2.具體代碼

Main
import java.io.*;
import java.net.MalformedURLException;
import java.net.*;
import java.util.*;

public class 測試程序{                                                                                                             
            

       
        public static void main(String[] args) throws Exception{
          String url = "http://127.0.0.1:8081/uploadtimg.jpg";
          String path = "C:\\Users\\劉金豪\\Desktop\\copy.jpg";
          
          //下載一個文件
          DownloadManager.getInstance().loadData(url,path);
        }
}

DownloadManager

這個類用來分配任務

import java.util.Map;

public  class DownloadManager{
    private Map<String,String>[] source;
    private static DownloadManager manager;
    private static Object obj = new Object();
    private DownloadManager() {
        
    }
    
    public static DownloadManager getInstance() {
        if(manager == null) {
            synchronized(obj) {
                if(manager == null) {
                    manager = new DownloadManager();
                }
            }
        }
        return manager;
    }
    
    //下載一個圖片(資源比較少)
    public void loadData(String urlString,String filePath) {//從哪下到哪
        DownloadOperation downloader = new DownloadOperation(urlString,filePath,3);
        downloader.download();
        new Thread(new Runnable() {

            public void run() {
                while(true) {
                    float rate = downloader.currentRate();
                    if(rate < 1.000001) {
                     System.out.println("當前下載比例"+ downloader.currentRate());
                         try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        break;
                    }
                }
            }
        }).start();
    }
    
    //下載多個資源
    public void loadData(Map<String,String>[] datas) {
        
    }
}


DownloadOperation

這個類用來做具體的下載任務

import java.io.*;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;

public class DownloadOperation {
  private URL url;
  private String filePath;//文件地址
  private int threadCount;//線程數(shù)量
  private DownloadThread[] tasks;//線程數(shù)組
  private long size;
  
  DownloadOperation(String urlString,String filePath,int threadCount){{
      try {
        this.url = new URL(urlString);
      } catch (Exception e) {
        e.printStackTrace();
      }
     
      this.filePath = filePath;
      this.threadCount = threadCount;
      tasks = new DownloadThread[threadCount];
  }
      
  }
  
  public void download() {
      //1.獲取文件的大小
      getFileSize();
      //System.out.println(size);
      
      //創(chuàng)建文件,用于保存下載的數(shù)據(jù)
      createFile();
      
      //分配線程下載數(shù)據(jù)
      dispatch();
  }
  private void dispatch() {
      //計算每個線程下載的平均大小
      long average = size / threadCount;
      
      long start = 0;
      long downloadSize = average;
      for(int i = 0;i < threadCount;i++){
          start = i * average;//默認下都是下average,但是最后一個線程可能不一樣
          
          //最后一個線程可能下載的數(shù)量要大于平均值
          if(i == threadCount - 1) {
              downloadSize = size - start;
          }
          
          //創(chuàng)建線程,執(zhí)行對應的模塊進行下載
          DownloadThread dt = new DownloadThread(url,filePath,start,downloadSize);
          //保存這個線程對象
          tasks[i] = dt;
          //啟動下載
          dt.start();
      }
  }
  
  //獲取當前下載比例,實現(xiàn)進度監(jiān)聽
  public float currentRate() {
      long len = 0;
      for(DownloadThread dt:tasks) {
          len += dt.downloadedLength;
      }
      return (float)len / size;
  }
  private void createFile() {
      File file = new File(filePath);
      
      try {
        file.createNewFile();//此時文件是0字節(jié)
    } catch (IOException e) {
        e.printStackTrace();
    }
      
      //設置文件大小
      RandomAccessFile rac = null;
      try {
        rac = new RandomAccessFile(file,"rw");
        rac.setLength(size);//此時創(chuàng)建的文件大小就是你要下載的文件大小了
      } catch (Exception e) {
        // TODO 自動生成的 catch 塊
        e.printStackTrace();
      }finally {
          try {
            rac.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
      }
  }
  
  private void getFileSize() {
      //url
      //獲取鏈接
      HttpURLConnection conn = null;
      try {
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("HEAD");//注意這里,請求時HEAD
        
        //獲取資源大小
        size = conn.getContentLengthLong();//獲取大小的方法
        

      } catch (IOException e) {
        e.printStackTrace();
      }finally {
        //關閉鏈接
        conn.disconnect();
    }
  } 
}


DownloadThread

skip()里面的參數(shù)是要跳過的字節(jié)數(shù)

package 對象;

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

public class DownloadThread extends Thread{
   private URL url;
   private String filePath;
   private long startPosition;
   private long size;//到底要下多少
   public long downloadedLength;//現(xiàn)在下了多少
   
   public DownloadThread(URL url,String filePath,long startPosition,long size) {
       this.url = url;
       this.filePath = filePath;
       this.startPosition = startPosition;
       this.size = size;
   }
   
   public void run() {
       //定位文件到這個線程應該寫入的位置
       try {
        RandomAccessFile raf = new RandomAccessFile(filePath,"rw");
        raf.seek(startPosition);
        
        //開始下載
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        conn.setConnectTimeout(5*1000);
        
        //獲取輸入流
        InputStream is = url.openStream();
        
        //設置數(shù)據(jù)讀取位置
        is.skip(startPosition);
        
        //開始讀取數(shù)據(jù) 寫入到文件
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = is.read(buffer)) != -1) {
            raf.write(buffer,0,len);
            
            //記錄當前下載的長度
            downloadedLength += len;
            
            //判斷當前線程下載的范圍是否結束
            if(downloadedLength > size) {
                break;
            }
        }
        
        //關閉資源
        is.close();
        conn.disconnect();
        raf.close();
       } catch (Exception e) {
        // TODO 自動生成的 catch 塊
        e.printStackTrace();
    }
   }
   
   
}

總結

這次學的內(nèi)容是非常之多,頭一次對網(wǎng)絡有了一點比較清晰的認識,但是內(nèi)容比較難消化,尤其是最后的多線程下載器項目。等空閑的時候我還要自己寫一遍,總結一下問題。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。