重拾Java基础知识:网络编程
網絡編程
- 前言
- 網絡體系結構
- IP地址
- IP地址分類
- 子網掩碼
- 端口
- 域名
- 網絡協議
- TCP/IP協議
- 三次握手與四次揮手
- 為什么要三次握手?
- 為什么要四次揮手?
- UDP協議
- HTTP協議
- Socket網絡編程
- TCP編程
- InetAddress 類
- Socket類
- ServerSocket類
- 案例一:客戶端向服務端發送消息,服務端收到消息后再發送給客戶端
- 案例二:客戶端向服務端發送圖片,服務端收到消息后再發送圖片給客戶端
- 案例三:客戶端和服務端通訊長連接
- UDP編程
- 案例一:發送方發送消息后,接收方接到消息
- 案例二:發送方和接收方長連接
- URL編程
- 創建
- 使用
- 連接
- 案例
- 本章小結
前言
計算機網絡20世紀60年代出現,進入21世紀后,計算機網絡已經成為信息社會的基礎設施,深入到人類社會的方方面面,與人們的工作、學習和生活息息相關。計算機網絡通過傳輸介質、通信設施和網絡通信協議,把分散在不同地點的計算機設備互連起來的,實現資源共享和數據傳輸的系統。
網絡編程就是編寫程序使互聯網的兩個(或多個)設備(如計算機)之間進行數據傳輸。Java語言對網絡編程提供了良好的支持。通過其提供的接口我們可以很方便地進行網絡編程。
網絡體系結構
計算機網絡體系結構是計算機網絡層次和協議的集合,網絡體系結構對計算機網絡實現的功能,以及網絡協議、層次、接口和服務進行了描述,但并不涉及具體的實現。接口是同一節點內相鄰層之間交換信息的連接處,也叫服務訪問點(SAP)。
IP地址
IP地址是IP協議提供的一種統一的地址格式,它為互聯網上的每一個網絡和每一臺主機分配一個邏輯地址,以此來屏蔽物理地址的差異。IP地址就像是我們的家庭住址一樣,如果你要寫信給一個人,你就要知道他(她)的地址,這樣郵遞員才能把信送到。
IP地址是一個32位的二進制數,通常被分割為4個“8位二進制數”(也就是4個字節)。IP地址通常用“點分十進制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之間的十進制整數。例:點分十進IP地址(100.4.5.6),實際上是32位二進制數(01100100.00000100.00000101.00000110)。
IP地址采用的IPv4格式,目前正在向IPv6過渡。IPv4采用32位地址長度,只有大約43億個地址,估計在2005~2010年間將被分配完畢,IPv4定義的有限地址空間將被耗盡,而地址空間的不足必將妨礙互聯網的進一步發展。而IPv6采用128位地址長度,幾乎可以不受限制地提供地址。按保守方法估算IPv6實際可分配的地址,整個地球的每平方米面積上仍可分配1000多個地址。在IPv6的設計過程中除解決了地址短缺問題以外,還考慮了在IPv4中解決不好的其它一些問題,主要有端到端IP連接、服務質量(QoS)、安全性、多播、移動性、即插即用等。
IP地址分類
IP地址是由網絡號(net ID)與 主機號(host ID)兩部分組成的。根據不同的取值范圍,IP地址可以分為五類;
比如:192.168.1.2 掩碼255.255.255.0 。網絡位192.168.1 主機位是2
子網掩碼
子網掩碼(subnet mask)又叫網絡掩碼、地址掩碼、子網絡遮罩,它用來指明一個IP地址的哪些位標識的是主機所在的子網,以及哪些位標識的是主機的位掩碼。子網掩碼不能單獨存在,它必須結合IP地址一起使用。
子網掩碼將A、B、C三類地址劃分為若干子網,從而顯著提高了IP地址的分配效率,有效解決了IP地址資源緊張的局面。另一方面,在企業內網中為了更好地管理網絡,網管人員也利用子網掩碼的作用,人為地將一個較大的企業內部網絡劃分為更多個小規模的子網,再利用三層交換機的路由功能實現子網互聯,從而有效解決了網絡廣播風暴和網絡病毒等諸多網絡管理方面的問題。
子網掩碼機制提供了子網劃分的方法。其作用是:減少網絡上的通信量;節省IP地址;便于管理;解決物理網絡本身的某些問題。使用子網掩碼劃分子網后,子網內可以通信,跨子網不能通信,子網間通信應該使用路由器,并正確配置靜態路由信息。劃分子網,就應遵循子網劃分結構的規則。就是用連續的1在IP地址中增加表示網絡地址,同時減少表示主機地址的位數。
比如利用子網數來計算:將B類IP:130.39.37.100劃分成27個子網。
端口
"端口"是英文port的意譯,可以認為是設備與外界通訊交流的出口。用兩個字節表示的整數,它的取值范圍是0 - 65535。如果說IP地址可以唯一的標識網絡中的設備,那么端口號就可以唯一標識設備中的應用程序,也就是應用程序的標識。常見的端口號有:tomcat(8080)、mysql(3306)等。
按照端口號的大小分類,可分為如下幾類:
- 公認端口(WellKnownPorts):從0到1023,它們緊密綁定(binding)于一些服務。通常這些端口的通訊明確表明了某種服務的協議。例如:80端口實際上總是HTTP通訊。
- 注冊端口(RegisteredPorts):從1024到49151。它們松散地綁定于一些服務。也就是說有許多服務綁定于這些端口,這些端口同樣用于許多其它目的。例如:許多系統處理動態端口從1024左右開始。
- 動態和/或私有端口(Dynamicand/orPrivatePorts):從49152到65535。理論上,不應為服務分配這些端口。實際上,機器通常從1024起分配動態端口。但也有例外:SUN的RPC端口從32768開始。
域名
由于IP地址具有不方便記憶并且不能顯示地址組織的名稱和性質等缺點,人們設計出了域名,并通過網域名稱系統(DNS,Domain Name System)來將域名和IP地址相互映射,使人更方便地訪問互聯網,而不用去記住能夠被機器直接讀取的IP地址數串。
從語法上講,每一個域名都是由標號序列組成,各標號之間用點隔開。如圖所示的域名,由3個標號組成,其中標號cn是頂級域名,標號com是二級域名,標號example是三級域名,www是主機名。
在國家頂級域名下注冊的二級域名由各國自行確定。我國把二級域名劃分為類別域名和行政域名2大類。
類別域名:如ac(科研機構),com(工、商、金融等企業),edu(中國教育機構),gov(中國政府機構),mil(中國國防機構),net(提供互聯網絡服務的機構),org(非營利性的組織)等
行政區域名:共34個,適用于我國的各省、自治區、直轄市,如bj(北京市),js(江蘇省),sn(陜西省),sx(山西省)等。
因特網的域名系統可以用域名樹來表示其結構,它實際上是一棵倒過來的樹,在最上面的是根,根下一級結點就是頂級域名,頂級域名可往下劃分二級域名,再往下劃分就是三級域名、四級域名。
域名到IP地址的解析是由分布在因特網上的許多域名服務器共同完成的。當某一個應用進程需要把主機名解析為IP地址時,該應用進程就調用解析程序,并將待解析的域名放在DNS請求報文中,以DUP用戶數據報的方式發送給本地域名服務器,本地域名服務器再找到域名后,將對應的IP地址放在回答報文中返回,應用進程獲得目的主機的IP地址后即可進行通信。
網絡協議
計算機網絡中實現通信必須有一些約定,即通信協議,對速率、傳輸代碼、代碼結構、傳輸控制步驟、出錯控制等制定標準。
最常用的復合方式是層次方式,網絡通信的不同方面被分解為多個層,層與層之間用接口連接。通信的雙方具有相同的層次,層次實現的功能由協議數據單元(PDU)來描述。不同系統中的同一層構成對等層,對等層之間通過對等層協議進行通信,理解批次定義好的規則和約定。每一層表示為物理硬件(即線纜和電流)與所傳輸信息之間的不同抽象層次。在理論上,每一層只與緊挨其上和其下的層對話。將網絡分層,這樣就可以修改甚至替換某一層的軟件,只要層與層之間的接口保持不變,就不會影響到其他層。
- OSI協議
開放式系統互聯通信參考模型(英語:Open System Interconnection Reference Model,縮寫為 OSI),簡稱為OSI模型(OSI model),一種概念模型,由國際標準化組織提出,一個試圖使各種計算機在世界范圍內互連為網絡的標準框架。定義于ISO/IEC 7498-1。
OSI模型把網絡通信的工作分為7層,分別是物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。
(1)物理層
物理層處于OSI的最底層,是整個開放系統的基礎。物理層涉及通信信道上傳輸的原始比特流(bits),它的功能主要是為數據端設備提供傳送數據的通路以及傳輸數據。
(2)數據鏈路層
數據鏈路層的主要任務是實現計算機網絡中相鄰節點之間的可靠傳輸,把原始的、有差錯的物理傳輸線加上數據鏈路協議以后,構成邏輯上可靠的數據鏈路。需要完成的功能有鏈路管理、成幀、差錯控制以及流量控制等。其中成幀是對物理層的原始比特流進行界定,數據鏈路層也能夠對幀的丟失進行處理。
(3)網絡層
網絡層涉及源主機節點到目的主機節點之間可靠的網絡傳輸,它需要完成的功能主要包括路由選擇、網絡尋址、流量控制、擁塞控制、網絡互連等。
(4)傳輸層
傳輸層起著承上啟下的作用,涉及源端節點到目的端節點之間可靠的信息傳輸。傳輸層需要解決跨越網絡連接的建立和釋放,對底層不可靠的網絡,建立連接時需要三次握手,釋放連接時需要四次揮手。
(5)會話層
會話層的主要功能是負責應用程序之間建立、維持和中斷會話,同時也提供對設備和結點之間的會話控制,協調系統和服務之間的交流,并通過提供單工、半雙工和全雙工3種不同的通信方式,使系統和服務之間有序地進行通信。
(6)表示層
表示層關心所傳輸數據信息的格式定義,其主要功能是把應用層提供的信息變換為能夠共同理解的形式,提供字符代碼、數據格式、控制信息格式、加密等的統一表示。
(7)應用層
應用層為OSI的最高層,是直接為應用進程提供服務的。其作用是在實現多個系統應用進程相互通信的同時,完成一系列業務處理所需的服務。
由于OSI參考模型過于龐大、復雜招致了許多批評。與此相對,美國國防部提出了TCP/IP協議棧參考模型,簡化了OSI參考模型,因為TCP/IP協議棧的簡單,獲得了廣泛的應用,并成為后續因特網使用的參考模型。
- TCP/IP協議
TCP/IP協議是一個開放的網絡協議簇,它的名字主要取自最重要的網絡層IP協議和傳輸層TCP協議。TCP/IP協議定義了電子設備如何連入因特網,以及數據如何在它們之間傳輸的標準。TCP/IP參考模型采用4層的層級結構,每一層都呼叫它的下一層所提供的協議來完成自己的需求,這4個層次分別是:網絡接口層、網絡層(IP層)、傳輸層(TCP層)、應用層。
(1)網絡接口層
這是TCP/IP軟件的最低層,負責接收IP數據報并通過網絡發送之,或者從網絡上接收物理幀,抽出IP數據報,交給IP層。
(2)網絡層
網絡層是整個TCP/IP協議棧的核心。它的功能是把分組發往目標網絡或主機。同時,為了盡快地發送分組,可能需要沿不同的路徑同時進行分組傳遞。因此,分組到達的順序和發送的順序可能不同,這就需要上層必須對分組進行排序。網絡層除了需要完成路由的功能外,也可以完成將不同類型的網絡(異構網)互連的任務。除此之外,互聯網層還需要完成擁塞控制的功能。
(3)傳輸層
負責在應用進程之間建立端到端的連接和可靠通信,它只存在與端節點中。涉及兩個協議,TCP和UDP。其中,TCP協議提供面向連接的服務,提供按字節流的有序、可靠傳輸,可以實現連接管理、差錯控制、流量控制、擁塞控制等。UDP協議提供無連接的服務,用于不需要或無法實現面向連接的網絡應用中。
(4)應用層
為各種網絡應用提供服務。
TCP/IP協議
TCP/IP協議報文段包括協議首部和數據兩部分,協議首部的固定部分是20個字節,首部的固定部分后面是選項部分。
下面是報文段首部各個字段的含義:
源端口號以及目的端口號:各占2個字節,端口是傳輸層和應用層的服務接口,用于尋找發送端和接收端的進程,一般來講,通過端口號和IP地址,可以唯一確定一個TCP/IP連接,在網絡編程中,通常被稱為一個socket接口。
序號:Seq序號,占4個字節、32位。用來標識從TCP/IP發送端向TCP/IP接收端發送的數據字節流。發起方發送數據時對此進行標記。
確認序號:Ack序號,占4個字節、32位。包含發送確認的一端所期望收到的下一個序號。只有ACK標記位為1時,確認序號字段才有效,因此,確認序號應該是上次已經成功收到數據字節序號加1,即Ack=Seq + 1。
數據偏移:占4個字節,用于指出TCP/IP首部長度,若不存在選項,則這個值為20字節,數據偏移的最大值為60字節。
保留字段占6位,暫時可忽略,值全為0。
標志位,6個
(1)URG(緊急):為1時表明緊急指針字段有效
(2)ACK(確認):為1時表明確認號字段有效
(3)PSH(推送):為1時接收方應盡快將這個報文段交給應用層
(4)RST(復位):為1時表明TCP連接出現故障必須重建連接
(5)SYN(同步):在連接建立時用來同步序號
(6)FIN(終止):為1時表明發送端數據發送完畢要求釋放連接
接收窗口:占2個字節,用于流量控制和擁塞控制,表示當前接收緩沖區的大小。在計算機網絡中,通常是用接收方的接收能力的大小來控制發送方的數據發送量。TCP/IP連接的一端根據緩沖區大小確定自己的接收窗口值,告訴對方,使對方可以確定發送數據的字節數。
校驗和:占2個字節,范圍包括首部和數據兩部分。
選項是可選的,默認情況是不選。
三次握手與四次揮手
TCP/IP是面向連接的協議,因此每個TCP/IP連接都有3個階段:連接建立、數據傳送和連接釋放。連接建立經歷三個步驟,通常稱為“三次握手”。三次握手的目的是同步連接雙方的序列號和確認號并交換 TCP/IP窗口大小信息。
客戶端發送連接請求。發送連接請求報文段內容:SYN=1,seq=x;SYN=1意思是一個TCP的SYN(同步序列)標志位置為1的包,指明客戶端打算連接的服務器的端口;seq=x表示客戶端初始序號x,保存在包頭的序列號(Sequence Number)字段里,進入SYN—SEND狀態,等待服務器的確認。
服務器收到客戶端連接請求報文,如果同意建立連接,向客戶機發回確認報文段(ACK)應答,并為該TCP連接分配TCP緩存和變量。服務器發回確認報文段內容:SYN=1,ACK=1,seq=y,ack=x+1;SYN標志位和ACK標志位均為1,同時將確認序號(Acknowledgement Number)設置為客戶的初始序列號 ( Initial Sequence Number) ISN 加1,即x+1;seq=y為服務端初始序號y。服務器端將上述所有信息放到一個報文段(即SYN+ACK報文段)中,一并發送給客戶端,此時服務器進入SYN—RECV狀態。
客戶機收到服務器的確認報文段后,向服務器給出確認報文段(ACK),并且也要給該連接分配緩存和變量。此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。客戶端發回確認報文段內容:ACK=1,seq=x+1,ack=y+1;ACK=1為確認報文段;seq=x+1為客戶端序號加1;ack=y+1,為服務器發來的ACK的初始序號字段+1。
為什么要三次握手?
- TCP/IP和UDP的區別
我們先來對比下TCP/IP和UDP的區別:我們都知道TCP/IP是可靠通信協議, 而 UDP 是不可靠通信協議,所以UDP會導致接收方接收到的數據可能存在部分丟失。
- 為什么不是兩次握手
TCP/IP是一個雙向通信協議, 通信雙方都有能力發送信息, 并接收響應。
(1)防止舊的重復連接引起連接混亂
如果 TCP/IP 握手的次數只有兩次,接收方只能選擇接受請求或者拒絕接受請求,在網絡狀況比較復雜或者網絡狀況比較差的情況下,發送方可能會連續發送多次建立連接的請求,某個網絡結點長時間的滯留了,在某個時間節點到達服務端,此時就有可能任務是服務端發起的新請求,向客戶端發生確認報文,建立連接,導致錯誤的連接。所以如果 TCP/IP 是三次握手的話,那么客戶端在接收到服務器端 SEQ+1 的消息之后,就可以判斷當前的連接是否為歷史連接,如果判斷為歷史連接的話就會發送終止報文(RST)給服務器端終止連接,否則就會發送指令給服務器端來建立連接。
(2)同步初始化序列化
通過上面的概念我們知道 TCP/IP的一個重要特征就是可靠性,而 TCP/IP為了保證在不穩定的網絡環境中構建一個穩定的數據連接,它就需要一個“序列號”字段來保證自己的穩定性,而這個序列號的作用就是防止數據包重復發送,以及有效的解決數據包接收時順序顛倒的問題。客戶端和服務端通過三次握手建立連接后,得到一個可靠的初始化序列號。如果只是兩次握手, 最多只有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認。
- 為什么不是四次或更多次握手
當服務端收到客戶端的SYN連接請求報文后,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。所以沒有必要將SYN和ACK單獨分開,避免資源浪費,三次握手是建立雙方連接的最低值。
當客戶端和服務器通過三次握手建立了TCP連接以后,就可以進行數據傳輸,當數據傳送完畢,就要進行關閉操作,需要經歷四個步驟。
由于TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務后就能發送一個FIN來終止這個方向的連接。收到一個FIN只意味著這一方向上沒有數據流動,一個TCP連接在收到一個FIN后仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
TCP客戶端發送一個FIN,用來關閉客戶端到服務端的數據傳送,客戶端進入FIN-WAIT-1狀態,表示客戶端沒有數據要發送給服務端了。
服務端收到這個FIN,它發回一個ACK給客戶端,確認序號為收到的序號加1。和SYN一樣,一個FIN將占用一個序號;服務端進入CLOSE-WAIT狀態,服務端告訴客戶端,我“同意”你的關閉請求。
服務端向客戶端發送FIN報文段,請求關閉連接,服務端進入LAST-ACK狀態。
客戶端收到FIN后,客戶端進入TIME-WAIT狀態,接著發回一個ACK報文給服務端確認,并將確認序號設置為收到序號加1,服務端進入CLOSED狀態,客戶端等待2MSL后依然沒有收到回復,則證明服務端已正常關閉,客戶端也可以關閉連接了,完成四次揮手。
為什么要四次揮手?
關閉連接時,客戶端向服務端發送 FIN 時,僅僅表示客戶端不再發送數據了但是還能接收數據。服務器收到客戶端的 FIN 報文時,先回一個 ACK 應答報文,而服務端可能還有數據需要處理和發送,等服務端不再發送數據時,才發送 FIN 報文給客戶端來表示同意現在關閉連接。
- 可以進行三次揮手嗎?
可以的,我們知道第三次揮手是為了確認服務端沒有要發送的數據,如果第一次揮手后服務端沒有數據要發送,是有可能將第二次和第三次揮手合并的,這樣就是三次揮手了。如果服務端有數據,難道就不能進行三次揮手了嗎?也是不一定的,TCP中有個延遲確認的特性,客戶端收到數據后,可以不用馬上進行ACK確認,可能將多個數據包合并一個確認包,再將確認包放到第四次揮手里,然后把第二次和第三次揮手合并,這樣也就是三次揮手了。
UDP協議
UDP 是User Datagram Protocol的簡稱, 中文名是用戶數據報協議,是OSI(Open System Interconnection,開放式系統互聯) 參考模型中一種無連接的傳輸層協議。傳輸數據之前源端和終端不建立連接,當它想傳送時就簡單地去抓取來自應用程序的數據,并盡可能快地把它扔到網絡上。在發送端,UDP傳送數據的速度僅僅是受應用程序生成數據的速度、計算機的能力和傳輸帶寬的限制;在接收端,UDP把每個消息段放在隊列中,應用程序每次從隊列中讀一個消息段。
- 由于傳輸數據不建立連接,因此也就不需要維護連接狀態,包括收發狀態等,因此一臺服務機可同時向多個客戶機傳輸相同的消息。
- UDP信息包的標題很短,只有8個字節,相對于TCP的20個字節信息包而言UDP的額外開銷很小。
- 吞吐量不受擁擠控制算法的調節,只受應用軟件生成數據的速率、傳輸帶寬、源端和終端主機性能的限制。
- UDP是面向報文的。發送方的UDP對應用程序交下來的報文,在添加首部后就向下交付給IP層。既不拆分,也不合并,而是保留這些報文的邊界,因此,應用程序需要選擇合適的報文大小。
雖然UDP是一個不可靠的協議,但它是分發信息的一個理想協議。例如,在屏幕上報告股票市場、顯示航空信息等等。UDP也用在路由信息協議RIP(Routing Information Protocol)中修改路由表。在這些應用場合下,如果有一個消息丟失,在幾秒之后另一個新的消息就會替換它。UDP廣泛用在多媒體應用中。
HTTP協議
超文本傳輸協議(Hyper Text Transfer Protocol,HTTP)是一個簡單的請求-響應協議,HTTP是應用層協議,它通常運行在TCP之上。它指定了客戶端可能發送給服務器什么樣的消息以及得到什么樣的響應。請求和響應消息的頭以ASCII形式給出;而 消息內容則具有一個類似MIME的格式。這個簡單模型是早期Web成功的有功之臣,因為它使開發和部署非常地直截了當。
典型的HTTP事務處理有如下的過程:
- 默認情況下,客戶端在端口80打開與服務器的一個TCP連接,URL中還可以指定其他端口。
- 客戶端向服務器發送消息,請求指定路徑上的資源。這個資源包括一個首部,可選地(取決于請求的性質)還可以有一個空行,后面是這個請求的數據。
- 服務器向客戶端發送響應。響應以響應碼開頭,后面是包含數據的首部、一個空行以及所請求的文檔或錯誤消息。
- 客戶端和服務器關閉連接。
HTTP報文由從客戶機到服務器的請求和從服務器到客戶機的響應構成。請求報文格式如下:
- 請求報文
HTTP請求報文由請求行、首部行和實體主體組成。
- 響應報文
HTTP響應報文由狀態行、首部行和實體主體組成。
除了上面案例中的POST請求方法,還有很多其它請求方法,如圖所示:
在HTTP響應報文的例子中,我們可以看到狀態碼是200,表示響應成功。
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之間)。
HTTP和HTTPS的區別:
超文本傳輸協議HTTP協議被用于在Web瀏覽器和網站服務器之間傳遞信息。HTTP協議以明文方式發送內容,不提供任何方式的數據加密,如果攻擊者截取了Web瀏覽器和網站服務器之間的傳輸報文,就可以直接讀懂其中的信息,因此HTTP協議不適合傳輸一些敏感信息,比如信用開號、密碼等。
為了解決HTTP協議的這一缺陷,需要使用另一種協議:安全套接字層超文本傳輸協議HTTPS。為了數據傳輸的安全,HTTPS在HTTP的基礎上加入了SSL協議,SSL依靠證書來驗證服務器的身份,并為瀏覽器和服務器之間的通信加密。
HTTPS和HTTP的區別主要為以下四點:
- https協議需要到ca申請證書,一般免費證書很少,需要繳費。
- http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
- http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
- http的連接很簡單,是無狀態的;https協議是有ssl+http協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。
Socket網絡編程
Java的網絡編程主要涉及到的內容是套接字(Socket)編程。Socket是通信的基石,是支持TCP/IP協議的網絡通信的基本操作單元。它是網絡通信過程中端點的抽象表示,包含進行網絡通信必須的五種信息:連接使用的協議、本地主機的IP地址、本地進程的協議端口、遠程主機的IP地址、遠程進程的協議端口。Socket本身不算是協議,它只是提供了一個針對TCP或者UDP編程的接口。Socket是對端口通信開發的工具,它要更底層一些。
Socket編程主要涉及到客戶端和服務端兩個方面,首先是在服務器端創建一個服務器套接字(ServerSocket),并把它附加到一個端口上,服務器從這個端口監聽連接。端口號的范圍是0到65536,但是0到1024是為特權服務保留的端口號,我們可以選擇任意一個當前沒有被其他進程使用的端口。
客戶端請求與服務器進行連接的時候,根據服務器的域名或者IP地址,加上端口號,打開一個套接字。當服務器接受連接后,服務器和客戶端之間的通信就像輸入輸出流一樣進行操作。
TCP編程
java.net.IntAddress類封裝計算機的IP地址,沒有端口。java.net.Socket 類代表一個套接字,而且 java.net.ServerSocket 類為服務器程序提供了一種機制來監聽客戶端并和它們建立連接。
InetAddress 類
InetAddress類的兩個子類:Inet4Address、Inet6Address,一個用于表示IPV4協議,另一個表示IPV6協議
InetAddress類不能直接創建,通過靜態方法創建對象。列舉常用方法:
- getByName(String host):通過主機名或域名得到InetAddress對象
- getLocalHost():獲取本地主機的地址。
- getLoopbackAddress():獲取IPV4或者IPV6回環對象。
- getAllByName(String host):獲取指定主機名的所有IP地址的數組。
示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) {InetAddress inetAddress = InetAddress.getByName("localhost");System.out.println(inetAddress);inetAddress = InetAddress.getLocalHost();System.out.println(inetAddress);inetAddress = InetAddress.getLoopbackAddress();System.out.println(inetAddress);InetAddress[]inetAddress2 = InetAddress.getAllByName("www.baidu.com");System.out.println(Arrays.toString(inetAddress2));/** Output: * localhost/127.0.0.1* DESKTOP-4K4SA9I/192.168.36.118* localhost/127.0.0.1* [www.baidu.com/180.101.49.12, www.baidu.com/180.101.49.11]*/} }創建后,還有許多方法可以進行獲取和判斷操作,列舉一部分代碼,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) {InetAddress inetAddress = InetAddress.getByName("www.baidu.com");System.out.println("規范的主機名:"+inetAddress.getCanonicalHostName());System.out.println("主機名:"+inetAddress.getHostName());System.out.println("是否為通配符地址:"+inetAddress.isAnyLocalAddress());System.out.println("是否為鏈接本地地址:"+inetAddress.isLinkLocalAddress());System.out.println("是否全局范圍的多播地址:"+inetAddress.isMCGlobal());System.out.println("是否鏈路本地作用域的多播地址:"+inetAddress.isMCLinkLocal());System.out.println("是否為一個IP組播地址:"+inetAddress.isMulticastAddress());System.out.println("地址是否可達:"+inetAddress.isReachable(1000));System.out.println("是否為網站本地地址:"+inetAddress.isSiteLocalAddress());/** Output:* 規范的主機名:180.101.49.12* 主機名:www.baidu.com* 是否為通配符地址:false* 是否為鏈接本地地址:false* 是否全局范圍的多播地址:false* 是否鏈路本地作用域的多播地址:false* 是否為一個IP組播地址:false* 地址是否可達:true* 是否為網站本地地址:false*/} }除此之外還有IntetSocketAddress類,與InetAddress 類不同的是增加了端口號(port),創建方法如下:
- createUnresolved(String host, int port):未解析的套接字地址 。
- new InetSocketAddress(int port):創建一個套接字地址,其中IP地址是通配符地址和指定值的端口號。
- new InetSocketAddress(String hostname, int port):使用主機名和端口號創建套接字地址。
- new InetSocketAddress(InetAddress addr, int port):使用InetAddress對象和端口號創建套接字地址。
示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) {InetSocketAddress inetSocketAddress = InetSocketAddress.createUnresolved("localhost",8088);System.out.println(inetSocketAddress);inetSocketAddress = new InetSocketAddress(8088);System.out.println(inetSocketAddress);inetSocketAddress = new InetSocketAddress("192.168.16.118",8088);System.out.println(inetSocketAddress);InetAddress inetAddress = InetAddress.getByName("192.168.16.118");inetSocketAddress = new InetSocketAddress(inetAddress,8088);System.out.println(inetSocketAddress);/** Output:* localhost:8088* 0.0.0.0/0.0.0.0:8088* /192.168.16.118:8088* /192.168.16.118:8088*/} }當然IntetSocketAddress類也有屬于自己的方法,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) {InetSocketAddress inetSocketAddress = new InetSocketAddress("www.baidu.com",8088);System.out.println("解析地址:"+inetSocketAddress.getAddress());System.out.println("主機名:"+inetSocketAddress.getHostName());System.out.println("端口號:"+inetSocketAddress.getPort());System.out.println("主機名或地址:"+inetSocketAddress.getHostString());System.out.println("是否解析:"+inetSocketAddress.isUnresolved());/** Output:* 解析地址:www.baidu.com/180.101.49.12* 主機名:www.baidu.com* 端口號:8088* 主機名或地址:www.baidu.com* 是否解析:false*/} }Socket類
Socket是一組編程接口(API), 是對TCP/IP協議的封裝和應用。java.net.Socket 類方法代表客戶端和服務器都用來互相通信的套接字。客戶端通過實例化而擁有一個 Socket 對象,然后服務器從 accept() 方法的返回值獲得一個 Socket 對象。
通過創建Socket 類可以監聽指定端口,列舉常用的構造函數,示例代碼如下:
- new Socket():創建一個未連接的套接字。使用 connect() 方法來連接這個套接字到服務器。
- new Socket(String host, int port):創建一個流套接字并將其連接到指定的主機和端口號。
- new Socket(InetAddress address, int port):創建一個流套接字并將其連接到指定的端口和指定的IP地址。
- new Socket(String host, int port, InetAddress localAddr,int localPort):創建套接字并將其連接到上指定的遠程主機指定的遠端端口。Socket也將bind()綁定到本地提供的地址和端口。
Socket類中還有一些比較常用的方法,示例代碼如下:
public class Client {public static void main(String[] args) {Socket socket = new Socket("localhost",3306);System.out.println("nio通道對象:"+socket.getChannel());System.out.println("是否啟用:"+socket.getKeepAlive());System.out.println("本地端口:"+socket.getLocalPort());System.out.println("遠程端口:"+socket.getPort());System.out.println("連接地址:"+socket.getInetAddress());System.out.println("本地地址:"+socket.getLocalAddress());System.out.println("遠程地址:"+socket.getRemoteSocketAddress());System.out.println("輸入流:"+socket.getInputStream());System.out.println("輸出流:"+socket.getOutputStream());System.out.println("是否連接成功:"+socket.isConnected());System.out.println("是否綁定成功:"+socket.isBound());System.out.println("是否關閉:"+socket.isClosed());System.out.println("讀取是否關閉:"+socket.isInputShutdown());System.out.println("寫入是否關閉:"+socket.isOutputShutdown());SocketAddress socketAddress = Proxy.NO_PROXY.address();//連接socket.connect(socketAddress);//綁定socket.bind(socketAddress);//關閉socket.close();//設置socket是否激活socket.setKeepAlive(true);//設置網絡緩沖區大小socket.setSendBufferSize(1024);/** Output:* nio通道對象:null* 是否啟用:false* 本地端口:58154* 遠程端口:3306* 連接地址:localhost/127.0.0.1* 本地地址:/127.0.0.1* 遠程地址:localhost/127.0.0.1:3306* 輸入流:java.net.SocketInputStream@29453f44* 輸出流:java.net.SocketOutputStream@5cad8086* 是否連接成功:true* 是否綁定成功:true* 是否關閉:false* 讀取是否關閉:false* 寫入是否關閉:false*/} }ServerSocket類
僅僅只有Socket類是不足以編寫服務器的,java.net.ServerSocket 類被服務器應用程序使用來獲得一個端口和監聽客戶端請求。ServerSocket使用accept()方法監聽這個端口的入站連接。accept()會一直阻塞,直到一個客戶端嘗試建立連接,此時accept()將會返回一個連接客戶端和服務器的Socket對象。
列舉常用的構造函數,示例代碼如下:
- new ServerSocket():創建未綁定的服務器套接字。
- new ServerSocket(int port):創建與指定端口綁定的服務器套接字。
- new ServerSocket(int port, int backlog):創建服務器套接字并將其綁定到指定的本地端口,設置請求的傳入連接隊列的最大長度。
- new ServerSocket(int port, int backlog, InetAddress bindAddr):創建一個具有指定端口的服務器,設置請求的傳入連接隊列的最大長度以及綁定的本地IP地址。
ServerSocket類中還有一些比較常用的方法,示例代碼如下:
public class Client {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(80);System.out.println("nio通道對象:"+serverSocket.getChannel());System.out.println("本地端口:"+serverSocket.getLocalPort());System.out.println("連接地址:"+serverSocket.getInetAddress());System.out.println("是否綁定成功:"+serverSocket.isBound());System.out.println("是否關閉:"+serverSocket.isClosed());//連接serverSocket.accept();//綁定serverSocket.bind(Proxy.NO_PROXY.address());//關閉serverSocket.close();/** Output:* nio通道對象:null* 本地端口:80* 連接地址:0.0.0.0/0.0.0.0* 是否綁定成功:true* 是否關閉:false*/} }案例一:客戶端向服務端發送消息,服務端收到消息后再發送給客戶端
客戶端,示例代碼如下:
public class Client {public static void main(String[] args) throws IOException {try(Socket socket = new Socket("192.168.36.118", 996);OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream();){System.out.println("客戶端發送消息:");outputStream.write("你好".getBytes());//關鍵代碼:告訴服務端結束輸出,否則服務端收到消息會一直阻塞socket.shutdownOutput();//outputStream.flush();byte[] bytes = new byte[1024];int len = 0;while ((len = inputStream.read(bytes)) != -1) {System.out.println("客戶端接收消息:" + new String(bytes, 0, len));}}catch (Exception e){}/** Output:* 客戶端發送消息:* 客戶端接收消息:你好,很高興認識你*/} }服務端,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) throws IOException {try(ServerSocket serverSocket = new ServerSocket(996);Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();) {byte[] bytes = new byte[1024];int len = 0;while ((len = inputStream.read(bytes)) != -1) {System.out.println("服務端接收消息:" + new String(bytes, 0, len));}System.out.println("服務端發送消息");outputStream.write("你好,很高興認識你".getBytes());}catch (Exception e){e.printStackTrace();}/** Output:* 服務端接收消息:你好* 服務端發送消息*/} }案例二:客戶端向服務端發送圖片,服務端收到消息后再發送圖片給客戶端
基本原理和發送消息一直,只不過把內容的主題換成了文件。
客戶端,示例代碼如下:
public class Client {public static void main(String[] args) throws IOException {byte[] bytes = new byte[1024];int len = 0;try (Socket socket = new Socket("192.168.36.118", 996);OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream();FileInputStream fileInputStream = new FileInputStream("C:\\mnt\\client.gif");FileOutputStream fileOutputStream = new FileOutputStream("client\\server.jpg");) {System.out.println("客戶端發送圖片");while ((len = fileInputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, len);}//關鍵代碼:告訴服務端結束輸出,否則服務端會一直阻塞socket.shutdownOutput();//outputStream.flush();System.out.println("收到服務端圖片并保存");while ((len = inputStream.read(bytes)) != -1) {fileOutputStream.write(bytes, 0, len);}} catch (Exception e) {}/** Output:* 客戶端發送圖片* 收到服務端圖片并保存*/} }服務端,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) throws IOException {byte[] bytes = new byte[1024];int len = 0;try(ServerSocket serverSocket = new ServerSocket(996);Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();FileInputStream fileInputStream = new FileInputStream("C:\\mnt\\server.jpg");FileOutputStream fileOutputStream = new FileOutputStream("server\\client.gif");) {System.out.println("收到客戶端圖片并保存");while ((len = inputStream.read(bytes)) != -1) {fileOutputStream.write(bytes,0,len);}System.out.println("服務端發送圖片");while ((len = fileInputStream.read(bytes)) != -1) {outputStream.write(bytes,0,len);}}catch (Exception e){e.printStackTrace();}/** Output:* 收到客戶端圖片并保存* 服務端發送圖片*/} }結果圖如下:
案例三:客戶端和服務端通訊長連接
先來展示錯誤的長連接方式。
客戶端,示例代碼如下:
public class Client {public static void main(String[] args) throws IOException {try(Socket socket = new Socket("192.168.131.1", 996);OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream();){while (true) {System.out.println("客戶端發送消息:");Scanner scanner = new Scanner(System.in);String b = scanner.next();outputStream.write(b.getBytes());//關鍵代碼:告訴服務端結束輸出,否則服務端收到消息會一直阻塞socket.shutdownOutput();//outputStream.flush();byte[] bytes = new byte[1024];int len = 0;while ((len = inputStream.read(bytes)) != -1) {System.out.println("客戶端接收消息:" + new String(bytes, 0, len));}}}catch (Exception e){e.printStackTrace();}/** Output:* 客戶端發送消息:* 11* 客戶端接收消息:222* 客戶端接收消息:222* 客戶端接收消息:qwe* 客戶端接收消息:qwe* 客戶端接收消息:qwe* 客戶端接收消息:qwe* 客戶端接收消息:qwe*/} }服務端,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) throws IOException {try(ServerSocket serverSocket = new ServerSocket(996);Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();) {while (true) {byte[] bytes = new byte[1024];int len = 0;while ((len = inputStream.read(bytes)) != -1) {System.out.println("服務端接收消息:" + new String(bytes, 0, len));}System.out.println("服務端發送消息");Scanner scanner = new Scanner(System.in);String b = scanner.next();outputStream.write(b.getBytes());}}catch (Exception e){e.printStackTrace();}/** Output:* 服務端接收消息:11* 服務端發送消息* 222* 服務端發送消息* 222* 服務端發送消息*/} }通過輸出可以看到,當客戶端第一次發送消息后,服務端接收完消息,再由服務端發送時,由于沒有關閉輸出操作,導致客戶端一直處于監聽狀態,但是如果服務端關閉輸出操作;客戶端再發送消息時,此時雙方發送都已關閉,這會導致異常錯誤。我們想要實現雙方都可以發送或接受消息,示例代碼如下:
客戶端,示例代碼如下:
public class Client {private static Executor executor = Executors.newCachedThreadPool();public static void main(String[] args) throws IOException {executor.execute(new Runnable() {@Overridepublic void run() {try (Socket socket = new Socket("192.168.131.1", 996);InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();) {while (true) {System.out.println("客戶端發送消息:");Scanner scanner = new Scanner(System.in);String s = scanner.nextLine();outputStream.write(s.getBytes());//強制刷新輸出outputStream.flush();byte[] bytes = new byte[1024];int len = inputStream.read(bytes);s = new String(bytes, 0, len);System.out.println("客戶端接收消息:" + s);}} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}});/** Output:* 客戶端發送消息:* 你好,很高興認識你!* 客戶端接收消息:我也是,怎么稱呼你?*/} }服務端,示例代碼如下:
public class NetworkProgramming {private static Executor executor = Executors.newCachedThreadPool();public static void main(String[] args) throws IOException {executor.execute(new Runnable() {@Overridepublic void run() {try (ServerSocket serverSocket = new ServerSocket(996);Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();) {while (true) {byte[] bytes = new byte[1024];int len = 0;while ((len = inputStream.read(bytes)) != -1) {String s = new String(bytes, 0, len);System.out.println("服務端接收消息:" + s);System.out.println("服務端發送消息:");Scanner scanner = new Scanner(System.in);s = scanner.nextLine();outputStream.write(s.getBytes());//強制刷新輸出outputStream.flush();}}} catch (Exception e) {e.printStackTrace();}}});/** Output:* 服務端接收消息:你好,很高興認識你!* 服務端發送消息:* 我也是,怎么稱呼你?*/} }這樣就實現了一個簡單的聊天工具,不過單方面通訊不能發送多條信息,算是一個小遺憾,有機會再研究,有想法的可以評論區討論!!!
UDP編程
UDP編程主要有兩個類:DatagramSocket類和DatagramPacket類,UDP數據報通過數據報套接字 DatagramSocket 發送和接收,系統不保證UDP數據報一定能夠安全送到目的地,也不能確定什么時候可以抵達。DatagramPacket 對象封裝了UDP數據報,在數據報中包含了發送端的 IP 地址和端口號以及接收端的IP地址和端口號。UDP協議中每個數據報都給出了完整的地址信息,因此無須建立發送方和接收方的連接。這里不在介紹每個類的創建方式和方法了,有興趣的可以自己去了解。
案例一:發送方發送消息后,接收方接到消息
發送方,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) throws IOException {try (DatagramSocket datagramSocket = new DatagramSocket();) {String str = "第一次發送數據";DatagramPacket datagramPacket = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("localhost"), 996);datagramSocket.send(datagramPacket);}} }接收方,示例代碼如下:
public class Client {public static void main(String[] args) throws IOException {try (DatagramSocket datagramSocket = new DatagramSocket(996);) {byte[] bytes = new byte[1024];//數據報包DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 996);datagramSocket.receive(datagramPacket);System.out.println("接收方接收數據:" + new String(bytes));}/** Output:* 接收方接收數據:第一次發送數據*/} }案例二:發送方和接收方長連接
發送方,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) throws IOException {//new DatagramSocket(996);監聽接收端口try (DatagramSocket datagramSocket = new DatagramSocket(997);) {while (true) {System.out.print("發送方發送數據:");String str = new Scanner(System.in).nextLine();DatagramPacket datagramPacket = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("localhost"), 996);datagramSocket.send(datagramPacket);byte[] bytes = new byte[1024];//數據報包datagramPacket = new DatagramPacket(bytes, bytes.length);datagramSocket.receive(datagramPacket);System.out.println("發送方接收數據:" + new String(bytes));}} catch (SocketException e) {e.printStackTrace();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}/** Output:* 發送方發送數據:你好,很高興認識你!* 發送方接收數據:我也是,要不出來見個面把。* 發送方發送數據:嗯嗯*/} }接收方,示例代碼如下:
public class Client {public static void main(String[] args) throws IOException {//new DatagramSocket(996);監聽接收端口try (DatagramSocket datagramSocket = new DatagramSocket(996);) {while (true) {byte[] bytes = new byte[1024];//數據報包DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);datagramSocket.receive(datagramPacket);System.out.println("接收方接收數據:" + new String(bytes));System.out.print("接收方發送數據:");String str = new Scanner(System.in).nextLine();datagramPacket = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("localhost"), 997);datagramSocket.send(datagramPacket);}} catch (SocketException e) {e.printStackTrace();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}/** Output:* 接收方接收數據:你好,很高興認識你!* 接收方發送數據:我也是,要不出來見個面把。* 接收方接收數據:嗯嗯*/} }經過TCP/IP和UDP代碼的代碼實戰,很容易發現它們之間的區別:TCP/IP建立一個端口就可以實現客戶端和服務端之間的交互,而UDP不同,發送方和接收發實際上是兩個不同的端口,只不過擔當同一個角色的兩個不同的操作,TCP/IP就有點類似于同一個角色同一個操作。
URL編程
URL(Uniform Resource Locator)中文名為統一資源定位符,有時也被俗稱為網頁地址。表示為互聯網上的資源,如網頁或者 FTP 地址。
創建
在java.net包中定義了URL類,該類用來處理有關URL的內容。
public class NetworkProgramming {public static void main(String[] args) throws IOException {URL url = new URL("https","baike.baidu.com",80,"/item/Java/85979?fr=aladdin#5");URL url = new URL("https","baike.baidu.com","/item/Java/85979?fr=aladdin#5");URL url = new URL(new URL("https://baike.baidu.com"),"/item/Java/85979?fr=aladdin#5");URL url = new URL("https://baike.baidu.com/item/Java/85979?fr=aladdin#5");} }一共有四種創建方式,以第一種創建方式為例:第一個參數表示協議、第二個參數表示主機、第三個參數表示端口、第四個參數表示文件地址。
使用
URL類中包含了很多方法用于訪問URL的各個部分:
public class NetworkProgramming {public static void main(String[] args) throws IOException {URL url = new URL("https://baike.baidu.com/item/Java/85979?fr=aladdin#5");System.out.println("url內容:" + url.getContent());System.out.println("url授權部分:" + url.getAuthority());System.out.println("url默認端口:" + url.getDefaultPort());System.out.println("url文件信息::" + url.getFile());System.out.println("url主機:" + url.getHost());System.out.println("url端口號:" + url.getPort());System.out.println("url協議:" + url.getProtocol());System.out.println("url參數:" + url.getQuery());System.out.println("url錨鏈接:" + url.getRef());System.out.println("url路徑:" + url.getPath());/** Output:* url內容:sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@66d2e7d9* url授權部分:baike.baidu.com* url默認端口:443* url文件信息::/item/Java/85979?fr=aladdin* url主機:baike.baidu.com* url端口號:-1* url協議:https* url參數:fr=aladdin* url錨鏈接:5* url路徑:/item/Java/85979*/} }連接
當你知道一個地址后,如果你需要使用它,就得先連接上:
public class NetworkProgramming {public static void main(String[] args) throws IOException {URL url = new URL("https://baike.baidu.com/item/Java/85979?fr=aladdin#5");URLConnection urlConnection = url.openConnection();} }連接成功后,可以獲取頁面的一些信息,下面列舉了比較常用的方法:
public class NetworkProgramming {public static void main(String[] args) throws IOException {URL url = new URL("https://baike.baidu.com/item/Java/85979?fr=aladdin#5");URLConnection urlConnection = url.openConnection();//設置地址可用于讀取urlConnection.setDoInput(true);//設置地址可用于輸出urlConnection.setDoOutput(true);//設置連接超時時間urlConnection.setConnectTimeout(1000);//設置讀取超時時間urlConnection.setReadTimeout(1000);System.out.println("獲取讀取狀態:"+urlConnection.getDoInput());System.out.println("獲取輸出狀態:"+urlConnection.getDoOutput());System.out.println("獲取連接狀態:"+urlConnection.getConnectTimeout());System.out.println("獲取讀取超時時間:"+urlConnection.getReadTimeout());System.out.println("獲取報文頭(header)內容長度:"+urlConnection.getContentLength());System.out.println("獲取報文頭(header)類型:"+urlConnection.getContentType());System.out.println("獲取報文頭(header)編碼格式:"+urlConnection.getContentEncoding());//輸入流urlConnection.getInputStream();//輸出流urlConnection.getOutputStream();/** Output:* 獲取讀取狀態:true* 獲取輸出狀態:true* 獲取連接狀態:1000* 獲取讀取超時時間:1000* 獲取報文頭(header)內容長度:-1* 獲取報文頭(header)類型:text/html; charset=UTF-8* 獲取報文頭(header)編碼格式:null*/} }案例
下面就介紹通過URL獲取某網站經典歌曲《兩只老虎》為例,示例代碼如下:
public class NetworkProgramming {public static void main(String[] args) throws IOException {URL url = new URL("http://music.163.com/song/media/outer/url?id=566443167.mp3");URLConnection urlConnection = url.openConnection();try (InputStream inputStream = urlConnection.getInputStream();FileOutputStream fileOutputStream = new FileOutputStream("兩只老虎.mp3");) {byte[] bytes = new byte[1024];int i = 0;while ((i = inputStream.read(bytes)) != -1) {fileOutputStream.write(bytes);}} catch (Exception e) {e.printStackTrace();}} }運行后,可以看到對應的項目工作空間下多了一個文件夾
本章小結
網絡編程從大的方面說就是對信息的發送到接收,中間傳輸為物理線路的作用。網絡編程設計的內容也是非常的廣,一起加油。
總結
以上是生活随笔為你收集整理的重拾Java基础知识:网络编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vimdiff常用命令详解
- 下一篇: i.MX6DL飞思卡尔双核安卓核心板主控