內(nèi)容
1.HTML??和Apache服務(wù)器后臺程序(PHP)如何進行交互
2.OSI七層模型(了解)
3.HTTP與TCP
4.請求(Request)和響應(yīng)(Response)
5.三次握手建立連接
6.IP地址與DNS
7.端口號
8.URL剩瓶、URLEncoder和URLDecoder
9.URL類的簡單使用
10.URL類下載圖片/視頻
11.多線程下載器項目(難點)
一.HTML??和Apache服務(wù)器后臺程序(PHP)如何進行交互
①我們在瀏覽器中輸???的地址宏胯,比如127.0.0.1/login.html奈附,進入網(wǎng)頁
②??中可以向服務(wù)器提交數(shù)據(jù)form表單
③服務(wù)器端使?對應(yīng)的?式接收表單中的數(shù)據(jù),比如 _GET["name"] 或_POST["name"]
④服務(wù)器使?echo返回給??數(shù)據(jù)
二.OSI七層模型(了解)
1.應(yīng)用層
網(wǎng)絡(luò)服務(wù)與最終用戶的一個接口辆苔。
協(xié)議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP
2. 表示層
數(shù)據(jù)的表示、安全攀隔、壓縮闲昭。(在五層模型里面已經(jīng)合并到了應(yīng)用層)
格式有堤框,JPEG夷陋、ASCll、EBCDIC胰锌、加密格式等
3. 會話層
建立骗绕、管理、終止會話资昧。(在五層模型里面已經(jīng)合并到了應(yīng)用層)
對應(yīng)主機進程酬土,指本地主機與遠程主機正在進行的會話
4. 傳輸層
定義傳輸數(shù)據(jù)的協(xié)議端口號,以及流控和差錯校驗格带。
協(xié)議有:TCP UDP撤缴,數(shù)據(jù)包一旦離開網(wǎng)卡即進入網(wǎng)絡(luò)傳輸層
5. 網(wǎng)絡(luò)層
進行邏輯地址尋址,實現(xiàn)不同網(wǎng)絡(luò)之間的路徑選擇叽唱。
協(xié)議有:ICMP IGMP IP(IPV4 IPV6)
6. 數(shù)據(jù)鏈路層
建立邏輯連接屈呕、進行硬件地址尋址、差錯校驗 等功能棺亭。(由底層網(wǎng)絡(luò)定義協(xié)議)
將比特組合成字節(jié)進而組合成幀虎眨,用MAC地址訪問介質(zhì),錯誤發(fā)現(xiàn)但不能糾正。
7. 物理層
建立嗽桩、維護岳守、斷開物理連接。(由底層網(wǎng)絡(luò)定義協(xié)議)
三.HTTP與TCP
1.HTTP協(xié)議:
是Hyper Text Transfer Protocol碌冶,即超文本傳輸協(xié)議湿痢,是一個基于請求與響應(yīng)模式、無狀態(tài)的網(wǎng)絡(luò)協(xié)議扑庞,是瀏覽器和服務(wù)器間最常用的通訊協(xié)議譬重。
HTTP協(xié)議包括兩部分,請求協(xié)議和響應(yīng)協(xié)議
2.URL與URI:
URL:Uniform Resource Locator罐氨,即統(tǒng)一資源定位符(可以理解成一個網(wǎng)址就是一個URL害幅,但是URL不一定就是網(wǎng)址),是互聯(lián)網(wǎng)上標(biāo)準(zhǔn)資源的地址岂昭,可以在全球范圍內(nèi)唯一確定的一個資源。
URI:Uniform Resource Identifier狠怨,即統(tǒng)一資源標(biāo)識符约啊,用于標(biāo)識一個資源的名稱。通過這種名稱命名的資源可以被互聯(lián)網(wǎng)定位和訪問
3.HTTP和TCP關(guān)系
①客戶端若要向服務(wù)器端發(fā)出請求佣赖,必須首先在它們之間建立一個TCP(Transfer Control Protocal恰矩,即傳輸控制協(xié)議)連接,當(dāng)客戶端與服務(wù)器端的通訊結(jié)束后憎蛤,TCP連接將被關(guān)閉外傅,而這個連接就是基于HTTP協(xié)議的。
②HTTP1.0在客戶端接收到服務(wù)器端發(fā)送來的響應(yīng)后俩檬,TCP馬上關(guān)閉萎胰,而HTTP1.1不同,客戶端在發(fā)送創(chuàng)建TCP連接請求之前首先計算出本次連接中棚辽,瀏覽器所要發(fā)送的請求數(shù)量技竟,即一次手工請求加上其所攜帶的所有自動請求數(shù)量,當(dāng)所有瀏覽器所發(fā)出的請求全部發(fā)送完畢后屈藐,客戶端會自動發(fā)送一個關(guān)閉TCP連接的請求榔组,這個請求在HTTPWatch中是看不到的。為了防止服務(wù)器主動將TCP連接關(guān)閉联逻,在每一個請求中都攜帶了一個參數(shù)Connection搓扯,用于告訴服務(wù)器是否關(guān)閉連接,在HTTPWatch中可以看到的這些請求中包归,其Connection參數(shù)值均為Keep-Alive保持連接锨推,只有當(dāng)客戶端發(fā)送了關(guān)閉TCP連接請求(Connection為close)時,服務(wù)器才會將TCP連接關(guān)閉
四.請求(Request)和響應(yīng)(Response)
1.注意:
請求和響應(yīng)是成對出現(xiàn)的
2.請求(Request):(一般是瀏覽器發(fā)出)
(1)何時發(fā)出請求?
一般情況下爱态,當(dāng)
①客戶端需要向服務(wù)器 上傳數(shù)據(jù)
②客戶端需要從服務(wù)器 下載數(shù)據(jù)
時谭贪,瀏覽器就會向服務(wù)器端發(fā)出請求,在這些請求中锦担,有用戶手動提出的請求俭识,也有瀏覽器自動發(fā)出的請求
(2)常用的請求方式:GET和POST
(3)GET請求
由于GET請求會將請求所攜帶的參數(shù)作為URL中的一部分出現(xiàn),所以請求參數(shù)會顯示在地址欄洞渔,這就導(dǎo)致了GET提交的三點不足
①參數(shù)值只能是字符串套媚,而不能是其他類型
②可以攜帶的數(shù)據(jù)量小
③數(shù)據(jù)安全性低
但GET請求有一個很重要的特征:客戶端一旦接收到“服務(wù)器向GET請求發(fā)送的響應(yīng)”后,瀏覽器會自動緩存響應(yīng)磁椒,當(dāng)客戶再次進行相同請求的提交時堤瘤,將直接讀取本地瀏覽器緩存中的數(shù)據(jù),而不再向服務(wù)器端真正發(fā)送數(shù)據(jù)浆熔,讓用戶感覺服務(wù)器端的響應(yīng)很快本辐,提升用戶體驗,減輕了服務(wù)器壓力医增。
(4)POST請求
POST請求會將請求所攜帶的數(shù)據(jù)以請求正文的形式出現(xiàn)(而不是在URL中)慎皱,所以與GET相比,就顯示出了幾點長處:
①數(shù)據(jù)類型可以是任意類型叶骨,還可以是聲音茫多、視頻、圖片等文件
②請求可以攜帶的數(shù)據(jù)量大(理論上沒有上限)
③數(shù)據(jù)安全性高
但發(fā)出POST請求的客戶端瀏覽器不會對接收到的“服務(wù)器向POST請求發(fā)送的響應(yīng)”進行緩存忽刽,所以當(dāng)用戶再次進行相同請求時天揖,仍是真正向服務(wù)器發(fā)送的請求,從服務(wù)器讀取的數(shù)據(jù)
(5)為何GET和POST不都有緩存跪帝?
主要原因是:以不同的方式提交請求今膊,其目的也是不同的
Ⅰ:GET(意為得到)請求的目的一般是客戶端要從服務(wù)器端下載資源,發(fā)送相同的請求就代表要下載相同的資源伞剑。既然要下載相同的資源万细,而這些資源已經(jīng)被下載到了客戶端,那么就無需再下載了纸泄,所以也就無需再向服務(wù)器發(fā)送真正的下載請求了赖钞。所以就將GET提交方式設(shè)計為了“GET請求的響應(yīng)結(jié)果會被瀏覽器緩存”。
Ⅱ:但POST(意為上傳)請求的目的一般是客戶端要向服務(wù)器端上傳資源聘裁,對于向服務(wù)器端上傳資源后的響應(yīng)結(jié)果雪营,瀏覽器是無需緩存的(緩存了沒有意義)
(6)GET和POST如何進行選擇
①當(dāng)提交時所攜帶的數(shù)據(jù)類型不是字符串(比如文件、文本衡便、音頻献起、對象等等)時
②提交時所攜帶的數(shù)據(jù)量比較大
③提交時所攜帶的數(shù)據(jù)具有敏感性洋访,安全性要求較高時
都用POST,其余情況均用GET
3.響應(yīng)Response:
響應(yīng)即服務(wù)器端對客戶端的請求作出的回應(yīng)
4.抓包工具:
如果想要查看地址欄所發(fā)出的請求詳情谴餐,那么我們可以通過抓包工具來攔截HTTP請求與響應(yīng)姻政,從工具中可以看到具體的請求與響應(yīng)內(nèi)容,抓包工具很多岂嗓,最常用的是HttpWatch
5.HEAD
HEAD:只是獲取服務(wù)器端返回的響應(yīng)信息 汁展,不會獲取具體的內(nèi)容
6.狀態(tài)碼:
①2xx:表示對請求計算與響應(yīng)成功,其中常用的狀態(tài)碼是200
②4xx:表示請求錯誤厌殉。其中常見的狀態(tài)碼是404食绿,表示資源找不到,一般都是請求路徑書寫有問題公罕。
③5xx:表示服務(wù)器端錯誤器紧,其中常見的狀態(tài)碼是500,表示服務(wù)器內(nèi)部有錯誤楼眷,一般都是服務(wù)器端的Java代碼發(fā)生錯誤
具體例子
200-206 請求成功
300-305 重定向 比如 www.baidu.com 然后跳轉(zhuǎn)到 www.qq.com
400-415 客戶端錯誤
500-505 服務(wù)器端錯誤
五.三次握?建?連接
HTTP封裝完數(shù)據(jù)之后铲汪,需要將數(shù)據(jù)使?TCP協(xié)議向?絡(luò)中的其他主機(服務(wù)器)進?發(fā)送
需要經(jīng)過三個過程/三次握?
可以概括為:①先問問有沒有(發(fā)送請求)(類似打電話開始播),②然后服務(wù)器告訴你有沒有(進行數(shù)據(jù)校驗)(類似打電話時提示 撥打的電話不存在或噔噔的聲音)③最后獲取數(shù)據(jù)(建立連接)(類似打電話的說話)
六.IP地址與DNS
①IP地址 就是?來唯?標(biāo)識?絡(luò)中的?臺設(shè)備罐柳,比如127.0.0.1
②域名就類似:www.baidu.com掌腰,域名和IP地址是一一對應(yīng)的關(guān)系,因為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: 訪問主機的那個?件或者?錄(也就是具體的服務(wù))
④?:分隔符 表示:需要向服務(wù)器提交數(shù)據(jù) 蔗崎。提交?式為:GET, 后?就是具體提交的數(shù)據(jù)
⑤user_name=jack就是提交的?個數(shù)據(jù) 扰藕。user_name是客戶端(html)定義的字段 缓苛。jack是字段對應(yīng)的數(shù)據(jù)
⑥使?&來連接多個字段
七.端?號
一個端?對應(yīng)的是?種服務(wù),對應(yīng)一個應(yīng)用程序邓深,比如QQ未桥。
一般來說,80端?對應(yīng)的是?絡(luò)服務(wù)
公認(rèn)端?:0-1023 芥备,用來進行?些特定的服務(wù)
注冊端?:1024-49151 自己寫的應(yīng)?程序要使?該范圍端?
動態(tài)私有端?:49152- 65535
八.URL冬耿、URLEncoder和URLDecoder
java使?URL類來封裝?絡(luò)數(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)絡(luò)數(shù)據(jù)
2.使用示例(向網(wǎng)絡(luò)中發(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)絡(luò)中發(fā)起請求
//1.獲取對應(yīng)的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();
//設(shè)置請求方式
conn.setRequestMethod("GET");//默認(rèn)GET
conn.setDoInput(true);//設(shè)置是否打開接收的流(默認(rèn)打開)
conn.setDoOutput(true);//設(shè)置是否打開輸出流 POST方式必須要打開
conn.setConnectTimeout(5*1000);//請求時間爱咬,如果5s還沒得到響應(yīng)信息,那么鏈接斷開
conn.setUseCaches(false);//設(shè)置是否緩存(如果緩存绊起,則下一次打開的時候會塊很多)
//3.接收數(shù)據(jù)(發(fā)送和接收都是用輸入輸出流)
//openStream 是從服務(wù)器端獲取數(shù)據(jù)
//conn.getOutputStream() 向服務(wù)器端發(fā)出數(shù)據(jù)
InputStream is = url.openStream();
//4.處理輸入流的數(shù)據(jù) 二進制(放到byte數(shù)組里面)然后轉(zhuǎn)換成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.關(guān)閉流
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.關(guān)閉流
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
這個類用來分配任務(wù)
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("當(dāng)前下載比例"+ downloader.currentRate());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}).start();
}
//下載多個資源
public void loadData(Map<String,String>[] datas) {
}
}
DownloadOperation
這個類用來做具體的下載任務(wù)
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;//默認(rèn)下都是下average,但是最后一個線程可能不一樣
//最后一個線程可能下載的數(shù)量要大于平均值
if(i == threadCount - 1) {
downloadSize = size - start;
}
//創(chuàng)建線程实蔽,執(zhí)行對應(yīng)的模塊進行下載
DownloadThread dt = new DownloadThread(url,filePath,start,downloadSize);
//保存這個線程對象
tasks[i] = dt;
//啟動下載
dt.start();
}
}
//獲取當(dāng)前下載比例荡碾,實現(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();
}
//設(shè)置文件大小
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 {
//關(guān)閉鏈接
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() {
//定位文件到這個線程應(yīng)該寫入的位置
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è)置數(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);
//記錄當(dāng)前下載的長度
downloadedLength += len;
//判斷當(dāng)前線程下載的范圍是否結(jié)束
if(downloadedLength > size) {
break;
}
}
//關(guān)閉資源
is.close();
conn.disconnect();
raf.close();
} catch (Exception e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
}
總結(jié)
這次學(xué)的內(nèi)容是非常之多局装,頭一次對網(wǎng)絡(luò)有了一點比較清晰的認(rèn)識坛吁,但是內(nèi)容比較難消化,尤其是最后的多線程下載器項目铐尚。等空閑的時候我還要自己寫一遍拨脉,總結(jié)一下問題。