目錄
1、前言
2、什麼是Socket
3、如何使用Socket進行http請求
一、創建socket鏈接
二、http協議請求和響應格式解析
三、進行http請求
4、寫在最後
java
本篇文章是爲講述okhttp源碼作一個鋪墊,主要是簡單講述一下socket的使用,由於在okhttp中網絡通信使用的即是socket。但這篇文章不會涉及okhttp,會簡單闡述下socket,而後用代碼進行鏈接後http通信,話很少說,開始幹!chrome
回答這個問題前咱們要先看下TCP/IP四層模型,想必這個圖你們都有見過,下面就解釋下這四層分別的表現形式是什麼(理論解釋比較讓人摸不着頭腦,因此這裏以其表現形式來闡述)json
Tip:順便說下TCP和UDP的區別。TCP提供可靠的通訊傳輸,相似於打電話,須要等待另外一方的接聽,才能進行真正的通信;而UDP則不是可靠的,相似發短信,只將信息發出,至於對方有沒收到,這個就不關心了。api
而咱們關心的socket是什麼呢?socket實際上是TCP鏈接的抽象,利用socket進行TCP的鏈接(這個解釋可能比較片面,但我的以爲是最爲直觀的解釋,畢竟全面的解釋比較晦澀難懂)bash
在java中使用socket,其實很是的簡單。若是隻是須要一個普通的socket,只需經過以下代碼,即可以創建一個socket鏈接服務器
Socket socket = new Socket(「ip或域名」, 端口);
複製代碼
若是想創建一個sslSocket,用於https的通信(例如:www.baidu.com)只須要經過sslSocketFactory進行建立sslSocket便可。代碼以下:網絡
Socket socket = SSLSocketFactory.getDefault().createSocket("www.baidu.com", 443);
複製代碼
在使用socket進行發起請求前,咱們要先來了解下http協議。簡單一點的理解,http協議其實就是發起一個按照格式約定的字符串,服務器響應一串按格式組裝的數據。 這裏不使用教科書式的數據格式,咱們使用從"Restlet Client"發起一次請求,觀察其請求報文和響應報文來進行講解。數據結構
Tip:Restlet Client是一個api請求工具,平常開發中也能夠用來向服務器發起請求,獲取數據結構方便調試。能夠在chrome的應用商店下載。app
這裏使用的api是高德的天氣預報接口,點擊了「send」後,獲取到請求報文和響應報文,以下圖所示socket
GET /v3/weather/weatherInfo?city=%E9%95%BF%E6%B2%99&key=13cb58f5884f9749287abbead9c658f2 HTTP/1.1
Host: restapi.amap.com
複製代碼
tip:這裏實際上是請求頭部,若是頭部參數有多個的話,就按照這種格式進行拼裝。例如還有一個「Connection爲keep-alive」的頭部參數,則以「Connection: keep-alive\r\n」的形式寫入輸出流中,具體會在後面的例子中展現。
city=長沙&key=13cb58f5884f9749287abbead9c658f2
複製代碼
至此一個請求報文便拼裝完畢,將其用輸出流寫出便可得到服務器的響應報文。
HTTP/1.1 200 OK
Server: Tengine
Date: Sun, 06 May 2018 08:22:10 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 445
Connection: close
X-Powered-By: ring/1.0.0
gsid: 010185222147152559493030300162313551811
sc: 0.013
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,key,x-biz,x-info,platinfo,encr,enginever,gzipped,poiid
{"status":"1","count":"2","info":"OK","infocode":"10000","lives":[{"province":"湖南","city":"長沙市","adcode":"430100","weather":"陣雨","temperature":"25","winddirection":"東北","windpower":"7","humidity":"78","reporttime":"2018-05-06 16:00:00"},{"province":"湖南","city":"長沙縣","adcode":"430121","weather":"陣雨","temperature":"25","winddirection":"東北","windpower":"7","humidity":"78","reporttime":"2018-05-06 16:00:00"}]}
複製代碼
這裏即是接口給咱們的數據,即此處給到咱們的天氣json數據,而此處json的長度爲頭部中有一個參數爲「Content-Length」決定的,例子中內容的長度爲445。值得一提的是,有些接口返回的頭部參數並無「Content-Length」這一頭部參數,而是返回了「Transfer-Encoding: chunked」這樣的頭部參數,則代表是以塊的形式給到咱們數據。 塊的形式會以以下格式,第一行的「10\r\n」代表接下來的一行會有10個字節的內容,第二行即是10字節的內容,一樣以「\r\n」結束一行(\r\n這兩個字符不算在內容長度中),每一塊的格式都按這樣的形式,若是遇到「0\r\n\r\n」就說明內容結束。
10\r\n //(注意!!!這裏是10是16進制,即若是進行內容讀取須要將其進行作10進制的轉換)
10字節長度的內容\r\n
//結束格式
0\r\n
\r\n
複製代碼
至此響應報文解析完畢。
逼逼叨逼逼叨了這麼久,不少小夥伴已經很火燒眉毛的想知道怎麼請求和獲取響應了。咱們這裏便直接上代碼,代碼很簡單,並無什麼知識難點。
public class MySocket {
public static void main(String[] args) throws IOException {
//若是須要進行https的請求只須要換成以下一句(https的默認端口爲443,http默認端口爲80)
//Socket socket = SSLSocketFactory.getDefault().createSocket("xxx", 443);
Socket socket = new Socket("restapi.amap.com", 80);
//獲取輸入流,即從服務器獲取的數據
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//獲取輸出流,即咱們寫出給服務器的數據
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//使用一個線程來進行讀取服務器的響應
new Thread() {
@Override
public void run() {
while (true) {
String line = null;
try {
while ((line = bufferedReader.readLine()) != null) {
System.out.println("recv : " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
bufferedWriter.write("GET /v3/weather/weatherInfo?city=%E9%95%BF%E6%B2%99&key=13cb58f5884f9749287abbead9c658f2 HTTP/1.1\r\n");
bufferedWriter.write("Host: restapi.amap.com\r\n\r\n");
bufferedWriter.flush();
}
}
複製代碼
跑起來後會看到控制檯輸出以下信息,這個時候咱們就能夠按照第二小結中的格式進行解析到一個模型中,最終返回給UI或是邏輯層去使用。
OkHttp中使用socket鏈接後,進行處理響應即是這樣的處理邏輯。只是它還有對socket的複用,鏈接進行限制之類的優化處理,這個在後面的文章中會進行剖析。若是您期待這樣的剖析之旅的話,給個「❤️」加個關注吧!文章中並無對頭部參數進行說明其含義,這裏也不打算給出,其實百度一下或google都有不少,須要的時候進行搜查一下便可。記住我,我是猛猛的小盆友😄,若是我有理解錯誤或是寫的晦澀難懂的地方請與我聯繫討論,共同進步。