Java—網絡編程總結(整理版)

1. 概述

  計算機網絡是經過傳輸介質、通訊設施和網絡通訊協議,把分散在不一樣地點的計算機設備互連起來的,實現資源共享和數據傳輸的系統。網絡編程就是編寫程序使互聯網的兩個(或多個)設備(如計算機)之間進行數據傳輸。Java語言對網絡編程提供了良好的支持。經過其提供的接口咱們能夠很方便地進行網絡編程。html

2. 網絡分層

  計算機網絡20世紀60年代出現,經歷了20世紀70年代、80年代和90年代的發展,進入21世紀後,計算機網絡已經成爲信息社會的基礎設施,深刻到人類社會的方方面面,與人們的工做、學習和生活息息相關。計算機網絡分爲網絡協議和網絡體系結構。java

2.1 網絡體系結構

  經過網絡發送數據是一項複雜的操做,必須仔細地協調網絡的物理特性以及所發送數據的邏輯特徵。經過網絡將數據從一臺主機發送到另外的主機,這個過程是經過計算機網絡通訊來完成。git

  網絡通訊的不一樣方面被分解爲多個層層與層之間用接口鏈接。通訊的雙方具備相同的層次,層次實現的功能由協議數據單元(PDU)來描述。不一樣系統中的同一層構成對等層,對等層之間經過對等層協議進行通訊,理解批次定義好的規則和約定。每一層表示爲物理硬件(即線纜和電流)與所傳輸信息之間的不一樣抽象層次。在理論上,每一層只與緊挨其上和其下的層對話。將網絡分層,這樣就能夠修改甚至替換某一層的軟件,只要層與層之間的接口保持不變,就不會影響到其餘層。程序員

  計算機網絡體系結構是計算機網絡層次和協議的集合,網絡體系結構對計算機網絡實現的功能,以及網絡協議、層次、接口和服務進行了描述,但並不涉及具體的實現。接口是同一節點內相鄰層之間交換信息的鏈接處,也叫服務訪問點(SAP)。github

                計算機網絡層次模型算法

  世界上第一個網絡體系結構由IBM公司提出(1974年,SNA),之後其餘公司也相繼提出本身的網絡體系結構。爲了促進計算機網絡的發展,國際標準化組織ISO在現有網絡的基礎上,提出了不基於具體機型、操做系統或公司的網絡體系結構,稱爲開放系統互連參考模型,即OSI/RM(Open System Interconnection Reference Model)。編程

  ISO制定的OSI參考模型過於龐大、複雜招致了許多批評。與此相對,美國國防部提出了TCP/IP協議棧參考模型,簡化了OSI參考模型,因爲TCP/IP協議棧的簡單,得到了普遍的應用,併成爲後續因特網使用的參考模型。瀏覽器

 2.1.1 OSI參考模型

這裏首先介紹OSI參考模型。OSI模型把網絡通訊的工做分爲7層,分別是物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。緩存

  

  • 物理層

  物理層處於OSI的最底層,是整個開放系統的基礎。物理層涉及通訊信道上傳輸的原始比特流(bits),它的功能主要是爲數據端設備提供傳送數據的通路以及傳輸數據。安全

  • 數據鏈路層

  數據鏈路層的主要任務是實現計算機網絡中相鄰節點之間的可靠傳輸,把原始的、有差錯的物理傳輸線加上數據鏈路協議之後,構成邏輯上可靠的數據鏈路。須要完成的功能有鏈路管理、成幀、差錯控制以及流量控制等。其中成幀是對物理層的原始比特流進行界定,數據鏈路層也可以對幀的丟失進行處理。

  • 網絡層

  網絡層涉及源主機節點到目的主機節點之間可靠的網絡傳輸,它須要完成的功能主要包括路由選擇、網絡尋址、流量控制、擁塞控制、網絡互連等。

  • 傳輸層

  傳輸層起着承上啓下的做用,涉及源端節點到目的端節點之間可靠的信息傳輸。傳輸層須要解決跨越網絡鏈接的創建和釋放,對底層不可靠的網絡,創建鏈接時須要三次握手,釋放鏈接時須要四次揮手。  

  • 會話層和表示層

  會話層的主要功能是負責應用程序之間創建、維持和中斷會話,同時也提供對設備和結點之間的會話控制,協調系統和服務之間的交流,並經過提供單工、半雙工和全雙工3種不一樣的通訊方式,使系統和服務之間有序地進行通訊。

  表示層關心所傳輸數據信息的格式定義,其主要功能是把應用層提供的信息變換爲可以共同理解的形式,提供字符代碼、數據格式、控制信息格式、加密等的統一表示。

  • 應用層

  應用層爲OSI的最高層,是直接爲應用進程提供服務的。其做用是在實現多個系統應用進程相互通訊的同時,完成一系列業務處理所需的服務。

 2.1.2 TCP/IP參考模型

  TCP/IP,即Transmission Control Protocol/Internet Protocol的簡寫,中譯名爲傳輸控制協議/因特網互聯協議,是Internet最基本的協議,Internet國際互聯網絡的基礎。

  TCP/IP協議是一個開放的網絡協議簇,它的名字主要取自最重要的網絡層IP協議和傳輸層TCP協議。TCP/IP協議定義了電子設備如何連入因特網,以及數據如何在它們之間傳輸的標準。TCP/IP參考模型採用4層的層級結構,每一層都呼叫它的下一層所提供的協議來完成本身的需求,這4個層次分別是:網絡接口層、網絡層(IP層)、傳輸層(TCP層)、應用層。

   

  • 網絡接口層

  TCP/IP協議對網絡接口層沒有給出具體的描述,網絡接口層對應着OSI參考模型的物理層和數據鏈路層

  • 網絡層(IP層)

  網絡層是整個TCP/IP協議棧的核心。它的功能是把分組發往目標網絡或主機。同時,爲了儘快地發送分組,可能須要沿不一樣的路徑同時進行分組傳遞。所以,分組到達的順序和發送的順序可能不一樣,這就須要上層必須對分組進行排序。網絡層除了須要完成路由的功能外,也能夠完成將不一樣類型的網絡(異構網)互連的任務。除此以外,互聯網層還須要完成擁塞控制的功能。

  • 傳輸層(TCP層)

  TCP層負責在應用進程之間創建端到端的鏈接和可靠通訊,它只存在與端節點中。TCP層涉及兩個協議,TCP和UDP。其中,TCP協議提供面向鏈接的服務,提供按字節流的有序、可靠傳輸,能夠實現鏈接管理、差錯控制、流量控制、擁塞控制等。UDP協議提供無鏈接的服務,用於不須要或沒法實現面向鏈接的網絡應用中。

  • 應用層

  應用層爲Internet中的各類網絡應用提供服務。

2.2 網絡協議

   如同人與人之間相互交流是須要遵循必定的規則(如語言)同樣,計算機之間可以進行相互通訊是由於它們都共同遵照必定的規則,即網絡協議。

  OSI參考模型和TCP/IP模型在不一樣的層次中有許多不一樣的網絡協議,如圖所示:

  

  網絡協議之間的關係圖以下:

  

 2.2.1 IP協議(Internet protocol)

   IP協議的做用在於把各類數據包準備無誤的傳遞給對方,其中兩個重要的條件是IP地址和MAC地址。因爲IP地址是稀有資源,不可能每一個人都擁有一個IP地址,因此咱們一般的IP地址是路由器給咱們生成的IP地址,路由器裏面會記錄咱們的MAC地址。而MAC地址是全球惟一的。舉例,IP地址就如同是咱們居住小區的地址,而MAC地址就是咱們住的那棟樓那個房間那我的。IP地址採用的IPv4格式,目前正在向IPv6過渡。

 2.2.2 TCP協議(Transmission Control Protocol)

  TCP(傳輸控制協議)是面向鏈接的傳輸層協議。TCP層是位於IP層之上,應用層之下的中間層。不一樣主機的應用層之間常常須要可靠的、像管道同樣的鏈接,可是IP層不提供這樣的流機制,而是提供不可靠的包交換。TCP協議採用字節流傳輸數據。

 2.2.2.1 TCP的報文格式

  TCP報文段包括協議首部和數據兩部分,協議首部的固定部分是20個字節,首部的固定部分後面是選項部分。

   

  下面是報文段首部各個字段的含義:

  1. 源端口號以及目的端口號:各佔2個字節,端口是傳輸層和應用層的服務接口,用於尋找發送端和接收端的進程,通常來說,經過端口號和IP地址,能夠惟一肯定一個TCP鏈接,在網絡編程中,一般被稱爲一個socket接口。
  2. 序號:Seq序號,佔4個字節、32位。用來標識從TCP發送端向TCP接收端發送的數據字節流。發起方發送數據時對此進行標記。
  3. 確認序號:Ack序號,佔4個字節、32位。包含發送確認的一端所指望收到的下一個序號。只有ACK標記位爲1時,確認序號字段纔有效,所以,確認序號應該是上次已經成功收到數據字節序號加1,即Ack=Seq + 1。
  4. 數據偏移:佔4個字節,用於指出TCP首部長度,若不存在選項,則這個值爲20字節,數據偏移的最大值爲60字節。
  5. 保留字段佔6位,暫時可忽略,值全爲0。
  6. 標誌位,6個
    • URG(緊急):爲1時代表緊急指針字段有效
    • ACK(確認):爲1時代表確認號字段有效
    • PSH(推送):爲1時接收方應儘快將這個報文段交給應用層
    • RST(復位):爲1時代表TCP鏈接出現故障必須重建鏈接
    • SYN(同步):在鏈接創建時用來同步序號
    • FIN(終止):爲1時代表發送端數據發送完畢要求釋放鏈接
  7.  接收窗口:佔2個字節,用於流量控制和擁塞控制,表示當前接收緩衝區的大小。在計算機網絡中,一般是用接收方的接收能力的大小來控制發送方的數據發送量。TCP鏈接的一端根據緩衝區大小肯定本身的接收窗口值,告訴對方,使對方能夠肯定發送數據的字節數。
  8. 校驗和:佔2個字節,範圍包括首部和數據兩部分。
  9. 選項是可選的,默認狀況是不選。

2.2.2.2 三次握手與四次揮手

  TCP是面向鏈接的協議,所以每一個TCP鏈接都有3個階段:鏈接創建、數據傳送和鏈接釋放。鏈接創建經歷三個步驟,一般稱爲「三次握手」。

  TCP三次握手過程以下:

  

  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。

  注意:握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。

  TCP四次揮手過程以下:

  

  因爲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。

  注意:爲何鏈接的時候是三次握手,關閉的時候倒是四次揮手?

    由於當服務端收到客戶端的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層。既不拆分,也不合並,而是保留這些報文的邊界,所以,應用程序須要選擇合適的報文大小。

 2.2.3.1 UDP協議格式

  

  UDP協議由兩部分組成:首部和數據。其中,首部僅有8個字節,包括源端口和目的端口、長度(UDP用於數據報的長度)、校驗和。

 2.2.3.2 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請求報文的格式

  

  HTTP請求報文由請求行、首部行和實體主體組成,由瀏覽器發送給服務器。上面這張圖中SP表示空格,cr lf表示回車和換行。下圖是谷歌瀏覽器內訪問服務器查看的HTTP請求例子:

  

   HTTP響應報文格式

   

  上面這張圖是HTTP響應報文,它由狀態行、首部行和實體主體組成。下圖爲HTTP響應報文例子:

  

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

  在上面的HTTP請求報文例子中,咱們能夠看到請求方法是GET,這表示請求讀取由URL所標誌的信息,除了GET,還有其餘幾種經常使用的方法。

  

  在HTTP響應報文的例子中,咱們能夠看到狀態碼是200,表示響應成功。下表是其餘狀態碼,總共5大類,33種。

  

 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之間)。

  超文本傳輸協議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協議安全。

 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到65536,可是0到1024是爲特權服務保留的端口號,咱們能夠選擇任意一個當前沒有被其餘進程使用的端口。

  客戶端請求與服務器進行鏈接的時候,根據服務器的域名或者IP地址,加上端口號,打開一個套接字。當服務器接受鏈接後,服務器和客戶端之間的通訊就像輸入輸出流同樣進行操做。

  

實例一

下面是一個客戶端和服務器端進行數據交互的簡單例子,客戶端輸入正方形的邊長,服務器端接收到後計算面積並返回給客戶端,經過這個例子能夠初步對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(); } } } }

上面改進後的服務器端代碼能夠支持不斷地併發響應網絡中的客戶請求。關鍵的地方在於多線程機制的運用,同時利用線程池能夠改善服務器程序的性能。

總結

【github地址】

https://github.com/SwordfallYeung/JavaNETDemo.git

【參考資料】

https://www.cnblogs.com/iOS-mt/p/4264675.html

https://www.cnblogs.com/xuan52rock/p/9454696.html

https://www.cnblogs.com/midiyu/p/7875574.html

https://www.cnblogs.com/dongguacai/p/5747397.html

https://blog.csdn.net/dosthing/article/details/83046052

https://blog.csdn.net/xuedan1992/article/details/80958522

相關文章
相關標籤/搜索