網絡編程——http協議


1、TCP/IP 基本知識

一、概念

TCP/IP協議是一個協議的集合,它是由眾多的互聯網相關聯的協議集合的總稱。如圖

 

二、TCP/IP分層管理

TCP/IP模型分為5層:應用層,傳輸層,網絡層,數據鏈路層,物理層。(注意:OSI分層是分為七層的)分層的最大好處,就是各層負責各層的任務,一旦某一塊出現問題,則可以直接替換對應的層即可,無需全部更改。

應用層:應用層是我們平時接觸最多的層,它的作用就是向用戶提供應用服務時通信的活動。比如說http協議,FTP協議,dns(域名解析)等協議,都是在該層

傳輸層:主要作用是提供處於網絡連接中的兩台計算機之間的數據傳輸。如tcp(可靠的傳輸控制協議),udp(用戶數據報協議)。傳輸單位是報文段。

網絡層:網絡層用來處理網絡中流動的數據包,數據包是網絡傳輸的最小單位。比如我們常用的IP協議,icmp協議,arp協議(通過IP地址得出對應的mac地址)都在該層。

數據鏈路層:一般用來處理連接硬件部分,如控制網卡,硬件相關的設備驅動。單位是數據幀

物理層:負責數據傳輸的硬件。比如說光纖等等

 

三、TCP/IP通信傳輸流

1、tcp/ip協議進行網絡通信時,會通過分層順序和對方進行通信。發送端從應用層往下走,接收端則從從下往上走。

2、發送端在層與層之間傳遞數據時,沒經過一層,就會打上相應層所屬的首部信息。而在接收端層與層之間傳遞數據時,沒經過一層,則會把對應的首部信息消去。這種把數據包裝起來的做法,叫做封裝。

四、和http關系密切的協議:IP、TCP、DNS

IP:IP位於網絡層,它是一種協議的名稱。注意和IP地址區分。它主要的作用就是把各種數據包傳遞給對方。要保證准確的傳遞到對方手中,其中有兩個重要的條件:1、IP地址     2、MAC地址  ,(IP地址可以和Mac地址進行配對,IP地址可以變換,但MAC地址,一般是不會變的)。

TCP:TCP位於傳輸層,是可靠的傳輸協議(連接需要三次握手,斷開需要四次揮手),它主要是提供可靠的字節流服務。1、字節流服務:為了傳輸方便,將大塊的數據分割以報文段為單位的數據包進行管理。 2、可靠的傳輸服務:它能夠把數據准確可靠的傳給對方

DNS服務:該服務是位於應用層的,主要的作用就是解析域名,可以將域名解析出對應的IP地址。

 

五、各種協議與HTTP協議之間的關系

 

2、http協議

一、http協議特點

http是位於應用層的一個超文本傳輸協議,基於tcp/ip通信協議來傳遞數據的。具有以下特點

1、簡單快速:客戶端向服務端請求服務時,只需要傳遞請求方法和路徑。由於http協議簡單,使得http服務器的程序規模小,因此通信速度快。

2、靈活:http允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type標記。

3、無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求以后,並且收到客戶端的應答后,就端開連接了。

4、無狀態:http協議是無狀態協議,也就是說對事務處理沒有記憶能力,缺少狀態,如果后續處理需要前面的信息,則必須要重新傳遞,這樣可能導致每次連接傳輸的數據量增大,但是在服務器不需要先前的數據的時候,就會快的多了。

5、支持B/S,C/S模式

 

二、http請求報文和響應報文

請求報文:http請求報文主要有請求行,請求頭,空行,請求體四部分組成

1、請求行:由請求方法(HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT),URL和協議版本組成。

2、請求頭:為請求報文添加一些信息,由名/值組成

3、空行:請求頭的最后會有一個空行,代表請求頭部結束,接下來是請求正文,此部分不可少。

4、請求正文

  

響應報文:http的響應報文由狀態行,響應頭部,空行,響應體組成

1、狀態行:由服務器HTTP協議版本,響應狀態碼,狀態碼的文本描述組成

2、響應頭部:和請求頭一樣,由名/值組成

3、請求頭的最后會有一個空行,代表請求頭部結束,接下來是請求正文,此部分不可少。

4、響應正文

 

請求方法

 

請求頭信息

 

 

響應頭

 

 

 

狀態碼分類

 

 

3、https

眾所周知,http是不安全的,它有着很大的缺陷,比如通信使用明文,不驗證通信方的身份,無法證明報文的正確性等。所以就有了https。https並不是一個新的協議。https全稱HTTP over TLS。這個TLS位於傳輸層的上層,應用層的下層,作為一個安全層而存在。

 

注:對於TLS和SSL的區別,大家可以自己去學習。在這里,只需要理解為TLS是SSL的升級版本就好。

 

一、https怎么加密的?

TLS是基於X.509認證的,他假定所有的數字證書都是由一個層次話的數字證書認證機構發出,即CA。他可以通過加密技術(對稱加密和非對稱加密)對我們傳輸的數據進行加密。不了解對稱加密和非對稱加密的朋友,可以看看我以前寫的這篇文章:https://www.cnblogs.com/huangjialin/p/9694488.html   CA用自己的私鑰簽發數字證書,數字證書中包含有公鑰,然后加密房就可以使用證書中的公鑰解密出CA簽發的證書,從中拿到合法的公鑰。是不是感覺有點懵逼。簡單來說,就是我們一般會在本地內置一個CA證書,然后用本地的CA證書中的公鑰去解密。拿到合法的公鑰,然后就可以用公鑰進行加密數據,然后服務端,就可以使用自己的私鑰就行解密拿到相關的數據。

注意:如果不內置本地證書可以嗎?可以,但是會存在比較大的漏洞,也就是很容易通過中間人攻擊,來截獲數據,所有,內置本地證書並且及時更新證書,這是一種比較有效的防止中間人攻擊的手段。

說了這么多,我們使用代碼來解釋一下,拿okhttp來做例子。okhttp添加加密證書的過程。

對okHttp進行簡單的配置,對OKhttp不熟悉的朋友可以看看這篇文章:https://www.cnblogs.com/huangjialin/p/9469373.html

 1 OkHttpClient.Builder builder = new OkHttpClient.Builder()
 2                 .sslSocketFactory() //配置ssl文件
 3                 .connectTimeout(15, TimeUnit.SECONDS)
 4                 .writeTimeout(20, TimeUnit.SECONDS)
 5                 .readTimeout(20, TimeUnit.SECONDS);
 6         OkHttpClient okHttpClient = builder.build();
 7         Request request = new Request.Builder()
 8                 .get() //設置請求模式
 9                 .url("https://www.baidu.com/")
10                 .build();
11 
12         Call call = okHttpClient.newCall(request);
13         call.enqueue(new Callback() {
14             @Override
15             public void onFailure(Call call, IOException e) {
16                 Log.d("MainActivity", "-----------onFailure-----------");
17             }
18 
19             @Override
20             public void onResponse(Call call, Response response) throws IOException {
21                 Log.d("MainActivity", "----onResponse----" + response.body().toString());
22                 runOnUiThread(new Runnable() {
23                     @Override
24                     public void run() {
25                         Toast.makeText(MainActivity.this, "請求成功", Toast.LENGTH_LONG).show();
26                     }
27                 });
28 
29             }
30         });

okhttp提供了一個方法sslSocketFactory(),專門用來設置ssl的。它需要傳遞一個SSLSocketFactory。

1  private synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
2         try {
3             SSLContext sslContext = SSLContext.getInstance("TLS");
4             sslContext.init(null, null, null);
5             return sslContext.getSocketFactory();
6         } catch (GeneralSecurityException e) {
7             throw new AssertionError(); 
8         }
9     }

上面的使用的是默認的SSLSocketFactory,也就是說什么也沒有配置,系統提供的。在校驗系統服務器的時候,會信任設備內置的100多個證書。那么他是怎么校驗的呢?主要是通過TrustManager這個類。在上面代碼sslContext.init(null, null, null);中,我們都填了null作為參數,但是這樣的話,都沒有辦法進行證書的校驗。我們看看內部源碼

1 public final void init(KeyManager[] km, TrustManager[] tm,
2                                 SecureRandom random)
3         throws KeyManagementException {
4         contextSpi.engineInit(km, tm, random);
5     }
 1 private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
 2         try {
 3             SSLContext sslContext = SSLContext.getInstance("TLS");
 4             sslContext.init(null, new TrustManager[]{
 5                     new X509TrustManager() {
 6                         public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
 7 
 8                         }
 9 
10                         public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
11                         }
12 
13                         public X509Certificate[] getAcceptedIssuers() {
14                             return new X509Certificate[0];
15                         }
16                     }
17             }, null);
18             return sslContext.getSocketFactory();
19         } catch (GeneralSecurityException e) {
20             throw new AssertionError();
21         }
22     }

實際上這樣配置,基本算是完成了。也沒錯,但是使用系統默認提供的SSLSocketFactory,它會默認設備中內置的100多個證書。基本上是什么證書都信任了,所以,還是存在很大的風險,中間人很容易就攻擊了。所以,我們還得自己配置自己的SSL證書。

 1 private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
 2         try {
 3             // 取到證書的輸入流  當然這里不一定要這樣讀取,就看你的證書存放在哪里了,huangjialin.crt是證書的名稱
 4             InputStream is = new FileInputStream("huangjialin.crt");
 5             CertificateFactory cf = CertificateFactory.getInstance("X.509");
 6             Certificate ca = cf.generateCertificate(is);
 7 
 8             // 創建 Keystore 包含我們的證書
 9             String keyStoreType = KeyStore.getDefaultType();
10             KeyStore keyStore = KeyStore.getInstance(keyStoreType);
11             keyStore.load(null);
12             keyStore.setCertificateEntry(null, ca);
13 
14             // 創建一個 TrustManager 僅把 Keystore 中的證書 作為信任的錨點
15             String algorithm = TrustManagerFactory.getDefaultAlgorithm();
16             TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
17             trustManagerFactory.init(keyStore);
18             TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
19 
20             // 用 TrustManager 初始化一個 SSLContext
21             SSLContext sslContext = SSLContext.getInstance("TLS");
22             sslContext.init(null, trustManagers, null);
23 
24             return sslContext.getSocketFactory();
25         } catch (GeneralSecurityException e) {
26             throw new AssertionError();
27         } catch (FileNotFoundException e) {
28             e.printStackTrace();
29             return null;
30         } catch (IOException e) {
31             e.printStackTrace();
32             return null;
33         }
34     }

通過我們自定義的SSL,自己設置信任錨點,安全性才會大大的提升。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM