網絡編程 -- 從 Socket 編程 到 OkHttp 框架

前言

最近在作一個項目的時候,由於項目要求跨域鏈接。因此,使用了Okhttp框架。其內部原理是基於 socket 網絡編程的。由於本身在這方面比較薄弱,因此寫這一篇文章進行相關的總結。html

基礎知識(參考 圖解TCP/IP 與 深刻理解計算機系統)

一、TCP/IP 參考模型
這位大佬寫的很詳細---點擊即看java

二、socket 套接字
每一個套接字都是鏈接的一個端點,有相應的套接字地址。由一個IP地址與16位的整數端口組成.一個鏈接由兩端的套接字地址惟一肯定。叫套接字對。
如:(cliaddr:cliport, servaddr:servport)
端口號分爲:
標準既定的端口號: 0~49151. 其中知名端口號由 0~1023 ** 組成。FTP 通常使用 21號端口號,HTTP 通訊通常使用 80 號端口號。
動態分配的端口號: 49152~65535. 操做系統爲之分配不一樣的端口號。而後應用程序使用時,由操做系統將鏈接創建。linux

三、java 中的網絡編程類android

InetAddress:用於標識網絡上的硬件資源,主要是IP地址
URL:統一資源定位符,經過URL能夠直接讀取或寫入網絡上的數據
Sockets:使用TCP協議實現的網絡通訊Socket相關的類
Datagram:使用UDP協議,將數據保存在用戶數據報中,經過網絡進行通訊。UDP協議中,使用 數據報 爲傳輸單位。

java 網絡編程類介紹

1. InetAddress

InetAddress類用於標識網絡上的硬件資源,標識互聯網協議(IP)地址。程序員

//獲取本機的InetAddress實例
InetAddress address =InetAddress.getLocalHost();
//獲取計算機名
address.getHostName();
//獲取IP地址
address.getHostAddress();
//獲取字節數組形式的IP地址,以點分隔的四部分
byte[] bytes = address.getAddress();

//獲取其餘主機的InetAddress實例
InetAddress address2 =InetAddress.getByName("其餘主機名");
InetAddress address3 =InetAddress.getByName("IP地址");

2. URL

URL(Uniform Resource Locator)統一資源定位符,表示Internet上某一資源的地址,協議名:資源名稱編程

  1. 基礎使用
//建立一個URL的實例
  URL myBlog =new URL("https://3dot141.cn");
  URL url =new URL(myBlog,"/blogs/33521.html?username=3dot141#test");//?表示參數,#表示錨點
  url.getProtocol();//獲取協議
  url.getHost();//獲取主機
  url.getPort();//若是沒有指定端口號,根據協議不一樣使用默認端口。此時getPort()方法的返回值爲 -1
  url.getPath();//獲取文件路徑
  url.getFile();//文件名,包括文件路徑+參數
  url.getRef();//相對路徑,就是錨點,即#號後面的內容
  url.getQuery();//查詢字符串,即參數
  1. 讀取網頁內容
//使用URL讀取網頁內容
  //建立一個URL實例
  URL url =new URL("http://www.baidu.com");
  InputStream is = url.openStream();//經過openStream方法獲取資源的字節輸入流
  InputStreamReader isr =newInputStreamReader(is,"UTF-8");//將字節輸入流轉換爲字符輸入流,若是不指定編碼,中文可能會出現亂碼
  BufferedReader br =newBufferedReader(isr);//爲字符輸入流添加緩衝,提升讀取效率
  String data = br.readLine();//讀取數據
  while(data!=null){
  System.out.println(data);//輸出數據
  data = br.readerLine();
  }
  br.close();
  isr.colose();
  is.close();

3. Socket

首先介紹下關於 linux 下的套接字鏈接原理,幫助理解json

下面介紹java 下 Socket的使用跨域

1.Socket 的構造方法

(1)Socket()

(2)Socket(InetAddress address, int port)throws UnknownHostException,IOException

// 設定遠程服務器地址與客戶端地址
(3)Socket(InetAddress address, int port, InetAddress localAddr, int localPort)throws IOException

(4)Socket(String host, int port) throws UnknownHostException,IOException

// 設定遠程服務器地址與客戶端地址
(5)Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException

2.獲取Socket信息

1. getInetAddress():得到遠程服務器的IP地址。

2. getPort():得到遠程服務器的端口。

3. getLocalAddress():得到客戶本地的IP地址。

4. getLocalPort():得到客戶本地的端口。

5. getInputStream():得到輸入流。若是Socket尚未鏈接,或者已經關閉,或者已經經過shutdownInput()方法關閉輸入流,那麼此方法會拋出IOException。

6. getOutputStream():得到輸出流。若是Socket尚未鏈接,或者已經關閉,或者已經經過shutdownOutput()方法關閉輸出流,那麼此方法會拋出IOException。

3.Socket 狀態

  1. 關閉狀態
1. close()

// 狀態測試方法
1. isClosed()
2. IsConnected()
3. isBound()
  1. 半關閉狀態
1. shutdownInput()
2. shutdownOutput()

// 狀態測試方法
1. isInputShutDown()
2. isOutputShutdown()

4.Socket 使用實例

以上就是 Socket 類的基本方法。 下面讓咱們進入實戰,來看一下,Socket 類如何使用數組

  1. 服務器端
/**
 * 基於TCP協議的Socket通訊,實現用戶登陸,服務端
*/
//一、建立一個服務器端Socket,即ServerSocket,指定綁定的端口,並監聽此端口
ServerSocket serverSocket =newServerSocket(33521);//1024-65535的某個端口
//二、調用accept()方法開始監聽,等待客戶端的鏈接
Socket socket = serverSocket.accept();
//三、獲取輸入流,並讀取客戶端信息
InputStream is = socket.getInputStream();
InputStreamReader isr =newInputStreamReader(is);
BufferedReader br =newBufferedReader(isr);
String info =null;
while((info=br.readLine())!=null){
System.out.println("我是服務器,客戶端說:"+info);
}
socket.shutdownInput();//關閉輸入流

//四、獲取輸出流,響應客戶端的請求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("歡迎您!");
pw.flush();


//五、關閉資源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
  1. 客戶端
//客戶端
//一、建立客戶端Socket,指定服務器地址和端口
Socket socket =newSocket("localhost",33521);
//二、獲取輸出流,向服務器端發送信息
OutputStream os = socket.getOutputStream();//字節輸出流
PrintWriter pw =newPrintWriter(os);//將輸出流包裝成打印流
pw.write("用戶名:3dot141;密碼:hahah");
pw.flush();
socket.shutdownOutput();
//三、獲取輸入流,並讀取服務器端的響應信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while((info=br.readLine())!null){
 System.out.println("我是客戶端,服務器說:"+info);
}

//四、關閉資源
br.close();
is.close();
pw.close();
os.close();
socket.close();
  1. 結果
我是服務器,客戶端說:用戶名:3dot141;密碼:hahah
我是客戶端,服務器說:歡迎您!
  1. 多線程中的運用服務器

    • 服務器端建立ServerSocket,使用while(true)循環調用accept()等待客戶端鏈接
    • 客戶端建立一個socket並請求和服務器端鏈接
    • 服務器端接受請求,建立socket與該客戶創建專線鏈接
    • 創建鏈接的兩個socket在一個單獨的線程上對話
    • 服務器端繼續等待新的鏈接
public class ServerThread implements runnable{
//服務器線程處理
//和本線程相關的socket
Socket socket =null;
//
public ServerThread(Socket socket){
this.socket = socket;
}

publicvoid run(){
//服務器處理代碼
}
}

//服務器代碼
ServerSocket serverSocket =newServerSocket(33521);
Socket socket =null;
int count =0;//記錄客戶端的數量
while(true){
socket = serverScoket.accept();
ServerThread serverThread =new ServerThread(socket);
 serverThread.start();
 count++;
System.out.println("客戶端鏈接的數量:"+count);
}

4. UDP 編程

1. 簡單介紹

UDP 是面向無鏈接的協議,反應迅速,適用於適時場景,可是丟包後不能發現。
用於 直播等網速要求較高的應用

DatagramSocket 端到端的通訊類.

//本機地址
// 隨機
DatagramSocket()
// 指定
DatagramSocket(int port, InetAddress)


// 發送與接收
send(DatagramPacket) 
receive(DatagramPacket)

DatagramPacket 數據報, 爲 IP 和 UDP 等網絡層以上的包的單位 。雖然這些都是包,但不一樣的層擁有不一樣的稱呼。數據鏈路層中 叫 , TCP 則表示 爲 .
方法

// 構造方法
// 接收時
DatagramPacket(byte[] buf, int length);
// 發送時
DatagramPacket(byte[] buf, int length, InetAddress iAdrr, int Port);

// 使用方法
// 用於服務器得到 客戶端地址
getAddress()
// 用於服務器得到 客戶端接口
getPort()

2. 基本使用

  1. 服務器端
//服務器端,實現基於UDP的用戶登陸
//一、建立服務器端DatagramSocket,指定端口
DatagramSocket socket =new datagramSocket(33521);
//二、建立數據報,用於接受客戶端發送的數據
byte[] data =newbyte[1024];//
DatagramPacket packet =newDatagramPacket(data,data.length);
//三、接受客戶端發送的數據
socket.receive(packet);//此方法在接受數據報以前會一致阻塞
//四、讀取數據
String info =newString(data,o,data.length);
System.out.println("我是服務器,客戶端告訴我"+info);


//=========================================================
//向客戶端響應數據
//一、定義客戶端的地址、端口號、數據
// 這裏也能夠本身設置
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "歡迎您!".geyBytes();
//二、建立數據報,包含響應的數據信息
DatagramPacket packet2 = new DatagramPacket(data2,data2.length,address,port);
//三、響應客戶端
socket.send(packet2);
//四、關閉資源
socket.close();
  1. 客戶端
//客戶端
//一、定義服務器的地址、端口號、數據
InetAddress address =InetAddress.getByName("localhost");
int port =33521;
byte[] data ="用戶名:3dot141;密碼:hahah".getBytes();
//二、建立數據報,包含發送的數據信息
DatagramPacket packet = newDatagramPacket(data,data,length,address,port);
//三、建立DatagramSocket對象
DatagramSocket socket =newDatagramSocket();
//四、向服務器發送數據
socket.send(packet);


//接受服務器端響應數據
//======================================
//一、建立數據報,用於接受服務器端響應數據
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2,data2.length);
//二、接受服務器響應的數據
socket.receive(packet2);
String raply = new String(data2,0,packet2.getLenth());
System.out.println("我是客戶端,服務器說:"+reply);
//四、關閉資源
socket.close();

OkHttp 框架

在項目中,我對 OkHttp 進行了簡單的封裝,基本知足我在項目中的須要。
下面貼上個人 工具類

public class OkhttpUtil {
    public static final MediaType JSON = MediaType.parse("application/json;charset=UTF-8");

    public static String doGet(String url) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request get = new Request.Builder().url(url).build();
        Response response = client.newCall(get).execute();
        return response.body().string();
    }

    public static String doGet(String url, Map<String, String> map) throws IOException {
        OkHttpClient client = new OkHttpClient();
        String newUrl = url;
        if (map != null) {
            int loop = 0;
            for (String key : map.keySet()) {
                if (loop == 0) {
                    newUrl = newUrl + "?" + key + "=" + map.get(key);
                } else {
                    newUrl = newUrl + "&" + key + "=" + map.get(key);
                }
                loop = 1;
            }
        }
        Request get = new Request.Builder().url(newUrl).build();
        Response response = client.newCall(get).execute();

        return response.body().string();
    }

    public static String doPost(String url, String requestBody) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request post = new Request.Builder().url(url).post(RequestBody.create(JSON, requestBody)).build();
        Response response = client.newCall(post).execute();
        if (!response.isSuccessful()) {
            throw new IOException("沒能獲得數據" + response);
        }
        return response.body().string();

    }
}

若是有對 okhttp 框架感興趣的,能夠參閱下面的網址。我就不獻醜了。
okhttp 源碼解析
okhttp 使用教程

結語

路漫漫其修遠兮,吾將上下而求索。
在程序員的道路上,我還只是一個剛上路的小學生,懷着對代碼世界的嚮往,砥礪前行。

stay hungry, stay foolish
與諸君共勉。
您的每一次點贊,關注都是對個人一種激勵。

個人我的博客 -- killCode謝謝。

相關文章
相關標籤/搜索