1. 程式人生 > 其它 >網路程式設計——http協議

網路程式設計——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,自己設定信任錨點,安全性才會大大的提升。

轉載請註明出處:https://www.cnblogs.com/huangjialin/