天天看點

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

轉載:Java—網絡程式設計總結(整理版)

文章目錄

  • 1 概述
    • 1.1 網絡通信的要素
    • 1.2 IP
    • 1.3 端口
    • 1.4 通信協定
  • 2 網絡分層
    • 2.1 網絡體系結構
      • 2.1.1 OSI參考模型
      • 2.1.2 TCP/IP參考模型
    • 2.2 網絡協定
      • 2.2.1 IP協定(網絡互連協定,Internet Protocol)
      • 2.2.2 TCP協定(使用者傳輸協定,Transmission Control Protocol)
      • 2.2.3 UDP協定(使用者資料報協定,User Datagram Protocol)
      • 2.2.4 HTTP協定(Hypertext Transfer Protocol)
        • 2.2.4.1 HTTP封包
        • 2.2.4.2 HTTP請求方法和響應狀态碼
        • 2.2.4.3 HTTP和HTTPS的差別
        • 2.2.4.4 HTTP和TCP/IP協定的關系
  • 3. Java Socket網絡程式設計
    • 3.1 Socket概述
    • 3.2 Socket整體流程
    • 3.3 狂 TCP Socket
    • 3.4 狂 UDP Socket
      • 3.4.1 簡單的例子
      • 3.4.2 循環發送
      • 3.4.3 多線程循環聊天
      • 3.4.4 URL
      • 3.4.4 URL

1 概述

計算機網絡是通過傳輸媒體、通信裝置和網絡通信協定,把分布在不同地點的計算機裝置互聯起來,實作資源共享和資料傳輸的系統。網絡程式設計就是編寫程式使網際網路的兩個(或多個)裝置之間進行資料傳輸。Java語言對網絡程式設計提供了良好的支援。通過其提供的接口,可以很友善的進行網絡程式設計。

1.1 網絡通信的要素

如何實作網絡通信?

通信雙方位址

​ ip

​ 端口号

規則:網絡通信協定

TCP/IP參考模型

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

小結:

  1. 網絡程式設計中的兩個要素
    1. 如何準确的定位到網絡上的一台或多台主機
    2. 找到主機之後如何進行通信
  2. 網絡程式設計中的要素
    1. IP 和端口号 IP
    2. 網絡通信協定 TCP UDP
  3. 萬物皆對象

1.2 IP

ip位址:ineyAddress

  1. 唯一定位網上一台網絡上的計算機
  2. 本機 localhost
  3. ip位址的分類
    1. ip位址分類:
      1. IPv4:由4個位元組組成,每個數字範圍0~255. 大概42億,30億在北美,亞洲4億。
      2. IPv6:128位。8個無符号整數
    2. 公網(網際網路)-私網(區域網路)
      1. ABCD類位址
  4. 域名:記憶IP問題
    1. IP:www.ip.com
import java.net.InetAddress;
import java.net.UnknownHostException;

// 測試IP
public class InetAddressDemo01 {
    public static void main(String[] args) {
        try {
            // 查詢本機位址
            InetAddress byName = InetAddress.getByName("127.0.0.1");
            System.out.println(byName);
            InetAddress byName2 = InetAddress.getByName("localhost");
            System.out.println(byName2);
            InetAddress byName3 = InetAddress.getLocalHost();
            System.out.println(byName3);
            // 查詢百度網站ip位址
            InetAddress byName1 = InetAddress.getByName("www.baidu.com");
            System.out.println(byName1);

            // 常用方法,但是沒啥用
            System.out.println(byName1.getAddress());       // [[email protected]
            // 獲得規範的名字
            System.out.println(byName1.getCanonicalHostName());     // 36.152.44.96
            // IP
            System.out.println(byName1.getHostAddress());       // 36.152.44.96
            // 域名
            System.out.println(byName1.getHostName());      // www.baidu.com
            
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}
           

1.3 端口

端口表示計算機上的程式的程序;

  1. 不同的程序有不同的端口号(Port ID),用來區分軟體。差別于程序ID(Process ID)
  2. 端口被規定0~65535
  3. TCP,UDP端口,65536 * 3,單協定下端口号不能沖突
  4. 端口分類
    1. 公有端口0~1023
      1. HTTP:80
      2. HTTPS:443
      3. FTP:21
      4. Telent(遠端監聽):23
    2. 程式注冊端口:1024~49151(配置設定給使用者或程式)
      1. Tomcat:8080
      2. MySql:3306
      3. Oracle:1521
    3. 私有(動态)端口:49152~65535
      netstat -ano	# 檢視所有的端口
      netstat -ano|findstr "5900"		# 檢視指定的端口
      tasklist|findstr "8696"			# 檢視特定端口的程序
                 

1.4 通信協定

協定:約定

網絡通訊協定:速率、傳輸碼率、傳輸控制

問題:複雜度高

2 網絡分層

計算機網絡出現于20世紀60年代。

分為網絡協定與網絡體系結構

2.1 網絡體系結構

計算機網絡通信:将網絡資料從一台主機發送至另外的主機

網絡通信的不同方面被了解為多個層,層與層之間用接口連接配接。通信的雙方具有相同的層次,層次實作的功能由協定資料單元(PDU)來描述。不同系統中的同一層構成對等層,對等層之間通過對等層協定進行通信,了解批次定義好的規則與約定。每一層表示為實體硬體(即線纜和電流)與所傳輸資訊之間的不同抽象層次。在理論上,每一層隻與其緊挨的上下層對話。将網絡分層,隻要保持接口不變,修改某層時便不會影響到其他層

計算機網絡體系結構是計算機網絡層次和協定的集合,網絡體系結構對計算機網絡實作的功能,以及網絡協定、層次、接口和服務進行了描述,但并不涉及具體的實作。接口是同一節點内相鄰層之間交換資訊的連接配接處,也叫服務通路點(SAP)。

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

1974年,IBM提出世界上第一個網絡結構體系SNA,其他公司緊随其後。

為促進計算機網絡的發展,國際标準化組織ISO,提出了不急于具體機型、作業系統或公司的網絡體系結構。稱為開放系統互連參考模型,即OSI/RM(Open System Interconnection Reference Model)。

ISO制定的OSI參考模型過于龐大、複雜。美國國防部提出了TCP/IP協定棧參考模型,簡化了OSI參考模型,成為後續網際網路使用的參考模型。

2.1.1 OSI參考模型

共7層:實體層、資料鍊路層、網絡層、傳輸層、會話層、表示層、應用層

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計
  1. 實體層
    1. 實體層:處于OSI的最底層,是整個開放系統的基礎。實體層涉及通信通道上傳輸的原始比特流(bits),它的功能主要是為資料端裝置提供傳輸資料的通路以及傳輸資料。
  2. 資料鍊路層
    1. 資料鍊路層:主要任務是實作計算機網絡中相鄰節點之間的可靠傳輸,把原始的、有差錯的實體傳輸線加上資料鍊路協定之後,構成邏輯上可靠的資料鍊路。需要完成的功能有鍊路管理、成幀、差錯控制和流量控制等。其中成幀是對實體層的原始比特流進行界定,資料鍊路層也能對幀的丢失進行處理。
  3. 網絡層
    1. 網絡層:設計源主機節點到目的主機節點之間可靠的網絡傳輸,主要功能包括:路由選擇、網絡尋址、流量控制、擁塞控制、網絡互連
  4. 傳輸層
    1. 傳輸層:起承上啟下的作用,設計源端節點到目的端節點之間可靠的資訊傳輸。傳輸層需要解決跨越網絡連接配接的建立和釋放,對底層不可靠的網絡,建立連接配接時需要三次握手,釋放連接配接時需要四次握手。
  5. 會話層和表示層
    1. 會話層:主要功能是負責應用程式之間建立、維持、中斷回話,同時也提供對裝置和節點之間的繪畫控制,協調系統和服務之間的交流,并通過提供單工、半雙工和全雙工通信方式,是系統和服務之間有序的進行通信。
    2. 表示層:主要功能是把應用層提供的資訊變換為能夠共同了解的形式,提供字元代碼、資料格式、控制資訊格式、加密等的同一表示。
  6. 應用層
    1. OSI的最高層,直接為應用程序提供服務,作用是在實作多個系統應用進村互相通信的同時,完成一系列業務處理所需的服務。

2.1.2 TCP/IP參考模型

TCP/IP:傳輸控制協定 / 網際網路互聯協定(Transmission Control Protocol / Internet Protocol),是Internet最基本的協定,Internet國際網際網路絡的基礎。

TCP/IP協定是一個開放的網絡協定簇,他的名字主要取自最重要的網絡層IP協定和傳輸層TCP協定。TCP/IP協定定義了電子裝置如何連入網際網路,以及資料如何在他們之間的傳輸的标準。TCP/IP參考模型采用4層的層級結構,每一層都呼叫他的下一層所提供的協定來完成自己的需求,分别為:網絡接口層、網絡層(IP層)、傳輸層(TCP層)、應用層。

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計
  1. 網絡接口層
    1. TCP/IP協定對網絡接口層沒有給出具體的描述,網絡接口層對應着OSI參考模型的實體層和資料鍊路層
  2. 網絡層(IP層)
    1. 網絡層是整個TCP/IP協定棧的核心。功能是把分組發往目标網絡或主機。同時,為盡快到達分組,可能需要沿不同路徑同時進行分組傳遞。是以分組到達的順序和發送的順序可能不同,這就需要上層必須對分組進行排序。網絡層除了需要完成路由的功能外,也可以完成将不同類型的網絡(異構網)互聯的任務。除此之外,網際網路層還需要完成擁塞控制的功能
  3. 傳輸層(TCP層)
    1. TCP層負責在應用程序之間建立端到端的連接配接和可靠通信,他隻存在端節點中。TCP層涉及兩個協定:TCP和UDP。
    2. TCP協定提供面向連接配接的服務,提供按位元組流的有序、可靠傳輸,可以實作連接配接管理、差錯控制、流量控制、擁塞控制等
    3. UDP協定提供無連接配接的服務,用于不需要或無法實作面向連接配接的網絡應用中
  4. 應用層
    1. 應用層為Internet中的各種網絡應用提供服務

2.2 網絡協定

網絡協定:計算機之間通信的協定規則

OSI參考模型與TCP/IP模型比較:

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

網絡協定之間的關系圖如下:

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

2.2.1 IP協定(網絡互連協定,Internet Protocol)

IP協定的作用:把各種資料包準确無誤的傳遞給對方。

重要條件:IP位址,MAC位址

路由器生成IP位址,并記錄MAC位址,MAC位址是唯一的

IP位址采用的IPv4格式,目前正在向IPv6過渡。

2.2.2 TCP協定(使用者傳輸協定,Transmission Control Protocol)

TCP(傳輸控制協定)是面向連接配接的傳輸層協定。位于IP上、應用層下的中間層。

TCP協定采用位元組流傳輸資料

  1. TCP封包形式

    TCO封包分為首部和資料兩部分,首部的固定部分是20位元組,後面是選項部分(1位元組=8bit)

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計
  1. **源端口号以及目的端口号 **:各占2位元組。端口是傳輸層和應用層的服務接口,用于尋找發送端和接收端的程序。一般來講,通過端口号和IP位址,可以唯一确定一個TCP連接配接,在網絡程式設計中,通常被稱為一個socket接口
  2. 序号:Seq序号,占4位元組。用來辨別TCP從發送端到接收端發送的資料位元組流。發起方發送資料時對此進行标記
  3. 确認序号:Ack序号,占4位元組。包含發送确認的一端所期望收到的下一個序号。隻有ACK标記位為1時,确認序号字段才有效。是以,确認序号應該是上次已經成功接收資料位元組序号加1,即Ack=Seq + 1
  4. 資料偏移:4位元組。指出TCP首部的長度,若不存在選項,則這個值為20位元組,資料偏移的最大值為60位元組。
  5. 保留字段:6位,值全為0
  6. 标志位:6個
    1. URG(緊急):為1時表明緊急指針字段有效
    2. ACK(确認):為1時表示确認号字段有效
    3. PSH(推送):為1時接收方應盡快将這個封包段交給應用層
    4. RST(複位):為1時表明TCP連接配接出現故障必須重新連接配接
    5. SYN(同步):在連接配接時用來同步信号
    6. FIN(終止):為1時表明發送端資料發送完畢要求釋放連接配接
  7. 接收視窗:2位元組。流量控制與擁塞控制,表示目前接收緩沖區的大小。
  8. 校驗和:2位元組,包括資料和首部兩部分
  9. 選項,預設不選
  10. 三次握手與四次揮手

    TCP是面向連接配接的協定,是以每個TCP連接配接都有3個階段:連接配接建立、資料傳送、連接配接釋放。連接配接建立經曆3個步驟,通常稱為“3次握手”

    ​ 3次握手過程如下:

    JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計
    1. 第一次握手(用戶端發送請求)

      用戶端發送連接配接請求封包段到服務端,進入SYN_SENT狀态,等待服務端确認。封包内容:SYN=1,seq=x. SYN=1意思是一個TCP的SYN标志位置為1的包,指明用戶端打算連接配接的伺服器的端口;seq=x表示用戶端初始序号x,儲存在標頭的序列号(Sequence Number)字段裡。

    2. 第二次握手(服務端回傳确認)

      服務端收到用戶端連接配接請求封包,如果同意建立連接配接,向客戶機發回确認封包段(ACK)應答,并為該TCP連接配接配置設定TCP緩存和變量。服務端發揮确認封包段内容:SYN=1,ACK=1,seq=y,ack=x+1.SYN标志位和ACK标志位均為1,同時将确認序号(Acknowledgement Number)設定為客戶的ISN加1,即x+1;seq=y為服務端初始序号y。

    3. 第三次握手(用戶端回傳确認)

      用戶端收到服務端的确認封包段後,向服務端給出确認封包段(ACK),并且也要給該連接配接配置設定緩存和變量。此包發送完畢,用戶端和伺服器進入ESTABLISHED(TCP連接配接成功)狀态,完成三次握手。用戶端發回确認封包段内容:ACK=1,seq=x+1,ack=y+1;ACK=1為确認封包段;seq=x+1為用戶端序号加1;ack=y+1,為伺服器發來的ACK的初始序号字段+1。

      注意:握手過程中傳送的包裡不包含資料,三次握手完畢後,用戶端與伺服器才正式開始傳送資料。

      A:你瞅啥
      B:瞅你咋地
      A:再瞅一個試試
                 
  11. 四次揮手:
JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

由于TCP連接配接是全雙工的,是以每個方向都必須單獨進行關閉。這原則是當一方完成它的資料發送任務後就能發送一個FIN來終止這個方向的連接配接。收到一個FIN隻意味着這一方向上沒有資料流動,一個TCP連接配接在收到一個FIN後仍能發送資料。首先進行關閉的一方将執行主動關閉,而另一方執行被動關閉。

  1. TCP用戶端發送一個FIN,用來關閉用戶端到服務端的資料傳送,用戶端進入FIN_WAIT_1狀态。發送封包段内容:FIN=1,seq=u;FIN=1表示請求切斷連接配接;seq=u為用戶端請求初始序号。
  2. 服務端收到這個FIN,它發回一個ACK給用戶端,确認序号為收到的序号加1。和SYN一樣,一個FIN将占用一個序号;服務端進入CLOSE_WAIT狀态。發送封包段内容:ACK=1,seq=v,ack=u+1;ACK=1為确認封包;seq=v為伺服器确認初始序号;ack=u+1為用戶端初始序号加1。
  3. 伺服器關閉用戶端的連接配接後,發送一個FIN給用戶端,服務端進入LAST_ACK狀态。發送封包段内容:FIN=1,ACK=1,seq=w,ack=u+1;FIN=1為請求切斷連接配接,ACK=1為确認封包,seq=w為服務端請求切斷初始序号。
  4. 用戶端收到FIN後,用戶端進入TIME_WAIT狀态,接着發回一個ACK封包給服務端确認,并将确認序号設定為收到序号加1,服務端進入CLOSED狀态,完成四次揮手。發送封包内容:ACK=1,seq=u+1,ack=w+1;ACK=1為确認封包,seq=u+1為用戶端初始序号加1,ack=w+1為伺服器初始序号加1。
A:我要斷開了
B:我知道你要斷開了
B:你斷開了嗎
A:我斷開了
           

注意:為什麼連接配接的時候是三次握手,關閉的時候卻是四次揮手?

​ 因為當服務端收到用戶端的SYN連接配接請求封包後,可以直接發送SYN+ACK封包。其中ACK封包是用來應答的,SYN封包是用來同步的。但是關閉連接配接時,當服務端收到FIN封包時,很可能并不會立即關閉socket,是以隻能先回複一個ACK封包,告訴用戶端,“你發的FIN封包,我收到了”。隻有等到服務端所有的封包都發送完了,我才能發送FIN封包,是以不能一起發送,故需要四步揮手。

2.2.3 UDP協定(使用者資料報協定,User Datagram Protocol)

UDP,使用者資料報協定,它是TCP/IP協定簇中無連接配接的運輸層協定。

  1. UDP是一個非連接配接的協定,傳輸資料之前源端和終端不建立連接配接,當它想傳送時就簡單地去抓取來自應用程式的資料,并盡可能快地把它扔到網絡上。在發送端,UDP傳送資料的速度僅僅是受應用程式生成資料的速度、計算機的能力和傳輸帶寬的限制;在接收端,UDP把每個消息段放在隊列中,應用程式每次從隊列中讀一個消息段。
  2. 由于傳輸資料不建立連接配接,是以也就不需要維護連接配接狀态,包括收發狀态等,是以一台伺服器可同時向多個用戶端傳輸相同的消息。
  3. UDP資訊包的标題很短,隻有8個位元組,相對于TCP的20個位元組資訊包的額外開銷很小。
  4. 吞吐量不受擁擠控制算法的調節,隻受應用軟體生成資料的速率、傳輸帶寬、源端和終端主機性能的限制。
  5. UDP使用盡量最大努力傳遞,即不保證可靠傳遞,是以主機不需要維持複雜的連結狀态表。
  6. UDP是面向封包的。發送方的UDP對應用程式交下來的封包,在添加首部受就向下傳遞給IP層。既不拆分,也不合并,而是保留這些封包的邊界,是以,應用程式需要選擇合适的封包大小。

UDP協定格式

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

UDP協定由兩部分組成:首部和資料。其中,首部僅有8個位元組,包括源端口和目的端口、長度(UDP用于資料報的長度)、校驗和。

TCP和UDP的差別

  1. TCP基于連接配接,UDP是無連接配接的;
  2. 對系統資源的要求,TCP較多,UDP較少;
  3. UDP程式結構較簡單;
  4. TCP是流模式,而UDP是資料報模式;
  5. TCP保證資料正确性,而UDP可能丢包;TCP保證資料順序,而UDP不保證;

2.2.4 HTTP協定(Hypertext Transfer Protocol)

HTTP,超文本傳輸協定,它是網際網路上應用最為廣泛的一種網絡協定。HTTP是一種應用層協定,它是基于TCP協定之上的請求/響應式的協定。HTTP協定是Web浏覽器和Web伺服器之間通信的标準協定。HTTP指定用戶端與伺服器如何建立連接配接、用戶端如何從伺服器請求資料,伺服器如何響應請求,以及最後如何關閉連接配接。HTTP連接配接使用TCP/IP來傳輸資料。

對于從用戶端到伺服器的每一個請求,都有4個步驟:

  1. 預設情況下,用戶端在端口80打開與伺服器的一個TCP連接配接,URL中還可以指定其他端口。
  2. 用戶端向伺服器發送消息,請求指定路徑上的資源。這個資源包括一個首部,可選地(取決于請求的性質)還可以有一個空行,後面是這個請求的資料。
  3. 伺服器向用戶端發送響應。響應以響應碼開頭,後面是包含資料的首部、一個空行以及所請求的文檔或錯誤消息。
  4. 伺服器關閉連接配接。

現在使用的HTTP協定是HTTP/1.1版本,1997年之前采用的是HTTP1.0版本。HTTP連接配接在1.0版本中采用非持續連接配接工作方式,1.1版本采用的是持續連接配接工作方式,持續連接配接是指伺服器在發送響應後仍然在一段時間内保持這條由TCP運輸層協定建立起來的連接配接,使用戶端和伺服器可以繼續在這條連接配接上傳輸HTTP封包。

是否采用持續連接配接工作方式,1.0中預設是關閉的,需要在HTTP頭加入“Connection:Keep-Alive”,才能啟用Keep-Alive。HTTP1.1中預設啟用Keep-Alive,如果加入“Connection:close”,才關閉。目前大部分浏覽器都是用HTTP1.1協定,也就是說預設都會發起Keep-Alive的連接配接請求了,是以是否能完成一個完整的Keep-Alive連接配接就看伺服器設定情況。

2.2.4.1 HTTP封包

HTTP協定是基于TCP協定之上的請求/響應式協定,下面主要介紹HTTP封包的格式,HTTP封包主要有請求封包和響應封包兩種。

首先看HTTP請求封包的格式:

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

HTTP請求封包由請求行、首部行和實體主體組成,由浏覽器發送給伺服器。上面這張圖中SP表示空格,cr lf表示回車和換行。下圖是谷歌浏覽器内通路伺服器檢視的HTTP請求例子:

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-0X7Q41Ws-1606119274904)(C:\Users\好氣氣\AppData\Roaming\Typora\typora-user-images\image-20201112155805855.png)]

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

HTTP響應封包格式:

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

上面這張圖是HTTP響應封包,它由狀态行、首部行和實體主體組成。下圖為HTTP響應封包例子:

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

2.2.4.2 HTTP請求方法和響應狀态碼

在上面的HTTP請求封包例子中,我們可以看到請求方法是GET,這表示請求讀取由URL所标志的資訊,除了GET,還有其他幾種常用的方法。

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

在HTTP響應封包的例子中,我們可以看到狀态碼是200,表示響應成功。下表是其他狀态碼,總共5大類,33種。

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計

2.2.4.3 HTTP和HTTPS的差別

HTTPS(全稱:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全為目标的HTTP通道,簡單來說就是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,是以加密的詳細内容就需要SSL。它是一個URL scheme(抽象辨別符體系),句法類同http:體系,用于安全的HTTP資料傳輸。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的預設端口及一個加密/身份驗證層(在HTTP與TCP之間)。

議以明文方式發送内容,不提供任何方式的資料加密,如果攻擊者截取了Web浏覽器和網站伺服器之間的傳輸封包,就可以直接讀懂其中的資訊,是以HTTP協定不适合傳輸一些敏感資訊,比如信用開号、密碼等。

為了解決HTTP協定的這一缺陷,需要使用另一種協定:安全套接字層超文本傳輸協定HTTPS。為了資料傳輸的安全,HTTPS在HTTP的基礎上加入了SSL協定,SSL依靠證書來驗證伺服器的身份,并為浏覽器和伺服器之間的通信加密。

HTTPS和HTTP的差別主要為以下四點:

  • https協定需要到ca申請證書,一般免費證書很少,需要繳費。
  • http是超文本傳輸協定,資訊是明文傳輸,https則是具有安全性的ssl加密傳輸協定。
  • http和https使用的是完全不同的連接配接方式,用的端口也不一樣,前者是80,後者是443。
  • http的連接配接很簡單,是無狀态的;https協定是有ssl+http協定建構的可進行加密傳輸、身份認證的網絡協定,比http協定安全。

2.2.4.4 HTTP和TCP/IP協定的關系

網絡中有一段比較容易了解的介紹:

“我們在傳輸資料時,可以隻使用(傳輸層)TCP/IP協定,但是那樣的話,如 果沒有應用層,便無法識别資料内容,如果想要使傳輸的資料有意義,則必須使用到應用層協定,應用層協定有很多,比如HTTP、FTP、TELNET等,也 可以自己定義應用層協定。WEB使用HTTP協定作應用層協定,以封裝HTTP文本資訊,然後使用TCP/IP做傳輸層協定将它發到網絡上。”

3. Java Socket網絡程式設計

3.1 Socket概述

Java的網絡程式設計主要涉及到的内容是Socket程式設計。Socket,套接字,就是兩台主機之間邏輯連接配接的端點。TCP/IP協定是傳輸層協定,主要解決資料如何在網絡中傳輸,而HTTP是應用層協定,主要解決如何包裝資料。Socket是通信的基石,是支援TCP/IP協定的網絡通信的基本操作單元。它是網絡通信過程中端點的抽象表示,包含進行網絡通信必須的五種資訊:連接配接使用的協定、本地主機的IP位址、本地程序的協定端口、遠端主機的IP位址、遠端程序的協定端口。

應用層通過傳輸層進行資料通信時,TCP會遇到同時為多個應用程式程序提供并發服務的問題。多個TCP連接配接或多個應用程式程序可能需要通過同一個TCP協定端口傳輸資料。為了差別不同的應用程式程序和連接配接,許多計算機作業系統為應用程式與TCP/IP協定互動提供了套接字(Socket)接口。應用層可以和傳輸層通過Socket接口,區分來自不同應用程式程序或網絡連接配接的通信,實作資料傳輸的并發服務。

Socket,實際上是對TCP/IP協定的封裝,Socket本身并不是協定,而是一個調用接口(API),通過Socket,我們才能使用TCP/IP協定。實際上,Socket跟TCP/IP協定沒有必然的關系,Socket程式設計接口在設計的時候,就希望也能适應其他的網絡協定。是以說,Socket的出現,隻是使得程式員更友善地使用TCP/IP協定棧而已,是對TCP/IP協定的抽象,進而形成了我們知道的一些最基本的函數接口,比如create、listen、accept、send、read和write等等。網絡有一段關于socket和TCP/IP協定關系的說法比較容易了解:

“TCP/IP隻是一個協定棧,就像作業系統的運作機制一樣,必須要具體實作,同時還要提供對外的操作接口。這個就像作業系統會提供标準的程式設計接口,比如win32程式設計接口一樣,TCP/IP也要提供可供程式員做網絡開發所用的接口,這就是Socket程式設計接口。”

實際上,傳輸層的TCP是基于網絡層的IP協定的,而應用層的HTTP協定又是基于傳輸層的TCP協定的,而Socket本身不算是協定,就像上面所說,它隻是提供了一個針對TCP或者UDP程式設計的接口。socket是對端口通信開發的工具,它要更底層一些。

3.2 Socket整體流程

Socket程式設計主要涉及到用戶端和服務端兩個方面,首先是在伺服器端建立一個伺服器套接字(ServerSocket),并把它附加到一個端口上,伺服器從這個端口監聽連接配接。端口号的範圍是0到65535,但是0到1024是為特權服務保留的端口号,我們可以選擇任意一個目前沒有被其他程序使用的端口。

用戶端請求與伺服器進行連接配接的時候,根據伺服器的域名或者IP位址,加上端口号,打開一個套接字。當伺服器接受連接配接後,伺服器和用戶端之間的通信就像輸入輸出流一樣進行操作。

JAVA網絡程式設計原理--TCP/UDP1 概述2 網絡分層3. Java Socket網絡程式設計
執行個體一

下面是一個用戶端和伺服器端進行資料互動的簡單例子,用戶端輸入正方形的邊長,伺服器端接收到後計算面積并傳回給用戶端,通過這個例子可以初步對Socket程式設計有個把握。

  • 伺服器端
public class SocketServer {
    public static void main(String[] args) throws IOException {

        // 端口号
        int port = 7000;
        // 在端口上建立一個伺服器套接字
        ServerSocket serverSocket = new ServerSocket(port);
        // 監聽來自用戶端的連接配接
        Socket socket = serverSocket.accept();

        DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));

        do {
            double length = dis.readDouble();
            System.out.println("伺服器端收到的邊長資料為:" + length);
            double result = length * length;
            dos.writeDouble(result);
            dos.flush();
        } while (dis.readInt() != 0);

        socket.close();
        serverSocket.close();
    }
}
           
  • 用戶端
public class SocketClient {
    public static void main(String[] args) throws UnknownHostException, IOException {

        int port = 7000;
        String host = "localhost";

        // 建立一個套接字并将其連接配接到指定端口号
        Socket socket = new Socket(host, port);

        DataInputStream dis = new DataInputStream(
                new BufferedInputStream(socket.getInputStream()));

        DataOutputStream dos = new DataOutputStream(
                new BufferedOutputStream(socket.getOutputStream()));

        Scanner sc = new Scanner(System.in);

        boolean flag = false;

        while (!flag) {

            System.out.println("請輸入正方形的邊長:");
            double length = sc.nextDouble();

            dos.writeDouble(length);
            dos.flush();

            double area = dis.readDouble();

            System.out.println("伺服器傳回的計算面積為:" + area);

            while (true) {

                System.out.println("繼續計算?(Y/N)");

                String str = sc.next();

                if (str.equalsIgnoreCase("N")) {
                    dos.writeInt(0);
                    dos.flush();
                    flag = true;
                    break;
                } else if (str.equalsIgnoreCase("Y")) {
                    dos.writeInt(1);
                    dos.flush();
                    break;
                }
            }
        }
        socket.close();
    }
}
           
執行個體二

可以看到上面的伺服器端程式和用戶端程式是一對一的關系,為了能讓一個伺服器端程式能同時為多個客戶提供服務,可以使用多線程機制,每個用戶端的請求都由一個獨立的線程進行處理。下面是改寫後的伺服器端程式。

public class SocketServerM {
    public static void main(String[] args) throws IOException {

        int port = 7000;
        int clientNo = 1;

        ServerSocket serverSocket = new ServerSocket(port);

        // 建立線程池
        ExecutorService exec = Executors.newCachedThreadPool();

        try {

            while (true) {
                Socket socket = serverSocket.accept();
                exec.execute(new SingleServer(socket, clientNo));
                clientNo++;
            }

        } finally {
            serverSocket.close();
        }

    }
}

class SingleServer implements Runnable {

    private Socket socket;
    private int clientNo;

    public SingleServer(Socket socket, int clientNo) {
        this.socket = socket;
        this.clientNo = clientNo;
    }

    @Override
    public void run() {

        try {

            DataInputStream dis = new DataInputStream(
                    new BufferedInputStream(socket.getInputStream()));

            DataOutputStream dos = new DataOutputStream(
                    new BufferedOutputStream(socket.getOutputStream()));

            do {

                double length = dis.readDouble();
                System.out.println("從用戶端" + clientNo + "接收到的邊長資料為:" + length);
                double result = length * length;
                dos.writeDouble(result);
                dos.flush();

            } while (dis.readInt() != 0);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("與用戶端" + clientNo + "通信結束");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
           

上面改進後的伺服器端代碼可以支援不斷地并發響應網絡中的客戶請求。關鍵的地方在于多線程機制的運用,同時利用線程池可以改善伺服器程式的性能。

3.3 狂 TCP Socket

  1. 用戶端
    1. 連接配接伺服器Socket
    2. 發送消息
    package networkProgramming.TCP;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    
    // 用戶端發送資料
    public class TcpClientDemo01 {
        public static void main(String[] args) throws IOException {
            // 3. 建立一個套接字,并将其連接配接到指定的端口,抛出異常
            int port = 7000;
            String host = "localHost";
            Socket socket = new Socket(host, port);
            // 4. 發送方發送消息,IO流
            OutputStream os = socket.getOutputStream();
            os.write("用戶端發送資料".getBytes());
            os.close();
            socket.close();
        }
    }
               
  2. 服務端
    1. 建立服務的端口ServerSocket
    2. 等待使用者的連接配接accept
    3. 接收使用者的消息
    package networkProgramming.TCP;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    // 服務端接收資料
    public class TcpServerDemo01 {
        public static void main(String[] args) throws IOException {
            // 1. 建立一個端口号
            int port = 7000;
            // 2. 在端口上建立一個服務端套接字,抛出異常
            ServerSocket serverSocket = new ServerSocket(port);
    
    
            // 5. 監聽來自用戶端的連接配接
            Socket accept = serverSocket.accept();
            // 6. 讀取用戶端的消息
            InputStream is = accept.getInputStream();
            /*
            // 普通寫法
            byte[] buffer = new byte[1024];
            int len;
            while((len = is.read(buffer)) != -1){
                String msg = new String(buffer, 0, len);
                System.out.println(msg);
            }
             */
            // 管道流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while((len = is.read(buffer)) != -1){
                baos.write(buffer,0, len);
            }
            System.out.println(baos.toString());
            baos.close();
            is.close();
            accept.close();
            serverSocket.close();
        }
    }
               

3.4 狂 UDP Socket

3.4.1 簡單的例子

發送端

package networkProgramming.UDP;

import java.io.IOException;
import java.net.*;

// 不需要連接配接伺服器

public class UdpClientDemo01 {
    public static void main(String[] args) throws IOException {
        // 1. 建立一個Socket
        DatagramSocket socket = new DatagramSocket();

        // 2. 建立包
        String msg = "UDP Client 發送資料";

        // 3. 發送給誰
        int port = 7000;
        InetAddress localhost = InetAddress.getByName("localHost");
        DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);

        // 4. 發送包
        socket.send(packet);

        // 5. 關閉源
        socket.close();

    }
}
           

接收端

package networkProgramming.UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

// 還是需要等待用戶端的連接配接
public class UdpServerDemo01 {
    public static void main(String[] args) throws IOException {
        // 1. 開放端口
        DatagramSocket datagramSocket = new DatagramSocket(7000);

        // 2. 接收資料包
        byte[] buffer = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
        datagramSocket.receive(datagramPacket);

        System.out.println(new String(datagramPacket.getData(), 0, datagramPacket.getLength()));

        // 3. 關閉連接配接
        datagramSocket.close();
    }
}
           

3.4.2 循環發送

發送

package networkProgramming.UDPChat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;


public class UdpSenderDemo01 {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(8888);

        while(true){
            // 準備資料。控制台讀取System.in
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

            // 打包資料
            String s = reader.readLine();
            byte[] bytes = s.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress("localhost",9999));

            // 發送資料
            socket.send(packet);

            // 退出機制
            if(reader.equals("bye")){
                break;
            }
        }
        socket.close();
    }
}
           

接收

package networkProgramming.UDPChat;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceiveDemo01 {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9999);
        while(true){
            // 準備接收資料包
            byte[] container = new byte[1024];
            DatagramPacket packet = new DatagramPacket(container, 0, container.length);
            // 阻塞式接收包
            socket.receive(packet);
            // 輸出
            byte[] data = packet.getData();
            String receive = new String(data, 0, data.length);
            System.out.println(receive);

            // 退出機制
            if(receive.equals("bye")){
                break;
            }
        }
        socket.close();
    }
}
           

3.4.3 多線程循環聊天

發送線程:

package networkProgramming.UDPChat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class TalkSend implements Runnable {
    DatagramSocket socket = null;
    BufferedReader reader = null;

    private int fromPort;
//    private String msgFrom;
    private String toIP;
    private int toPort;

    public TalkSend(int fromPort,/* String msgFrom,*/ String toIP, int toPort){
        this.fromPort = fromPort;
//        this.msgFrom = msgFrom;
        this.toIP = toIP;
        this.toPort = toPort;
        try {
            socket = new DatagramSocket(fromPort);
            // 準備資料。控制台讀取System.in
            reader = new BufferedReader(new InputStreamReader(System.in));
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while(true){
            try {
                // 打包資料
                String s = reader.readLine();
                byte[] bytes = s.getBytes();
                DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(this.toIP,this.toPort));
                // 發送資料
                socket.send(packet);
                /* 退出機制 */
                if("bye".equals(reader)){
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        socket.close();
    }
}
           

接收線程

package networkProgramming.UDPChat;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class TalkReceive implements Runnable{
    DatagramSocket socket = null;
    private int port;
    private String msgFrom;

    public TalkReceive(String msgFrom, int port) {
        this.msgFrom = msgFrom;
        this.port = port;
        try {
            socket = new DatagramSocket(port);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        while(true){

            try {
                // 準備接收資料包
                byte[] container = new byte[1024];
                DatagramPacket packet = new DatagramPacket(container, 0, container.length);
                // 阻塞式接收包
                socket.receive(packet);
                // 輸出
                byte[] data = packet.getData();
                String receive = new String(data, 0, data.length);
                System.out.println(msgFrom + ":" + receive);
                // 退出機制
                if(receive.equals("bye")){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        socket.close();
    }

}
           

Teacher對象

package networkProgramming.UDPChat;

public class TalkTeacher {
    public static void main(String[] args) {
        new Thread(new TalkSend(5555, /*"學生",*/ "localhost", 8888)).start();
        new Thread(new TalkReceive("學生", 9999)).start();
    }
}
           

Student對象

package networkProgramming.UDPChat;

public class TalkStudent {
    public static void main(String[] args) {
        new Thread(new TalkSend(6666, /*"學生",*/ "localhost", 9999)).start();
        new Thread(new TalkReceive("老師", 8888)).start();
    }
}
           

3.4.4 URL

統一資源定位符:定位資源的,定位網際網路上的某一個資源

DNS域名解析:www.baidu.com

協定://ip位址:端口/項目名/資源
           

ut.println(msgFrom + “:” + receive);

// 退出機制

if(receive.equals(“bye”)){

break;

}

} catch (IOException e) {

e.printStackTrace();

}

}
    socket.close();
}
           

}

Teacher對象

```java
package networkProgramming.UDPChat;

public class TalkTeacher {
    public static void main(String[] args) {
        new Thread(new TalkSend(5555, /*"學生",*/ "localhost", 8888)).start();
        new Thread(new TalkReceive("學生", 9999)).start();
    }
}
           

Student對象

package networkProgramming.UDPChat;

public class TalkStudent {
    public static void main(String[] args) {
        new Thread(new TalkSend(6666, /*"學生",*/ "localhost", 9999)).start();
        new Thread(new TalkReceive("老師", 8888)).start();
    }
}
           

3.4.4 URL

統一資源定位符:定位資源的,定位網際網路上的某一個資源

DNS域名解析:www.baidu.com

協定://ip位址:端口/項目名/資源