<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                現代的應用程序都離不開網絡,網絡編程是非常重要的技術。Java SE提供java.net包,其中包含了網絡編程所需要的最基礎一些類和接口。這些類和接口面向兩個不同的層次:基于Socket的低層次網絡編程和基于URL的高層次網絡編程,所謂高低層次就是通信協議的高低層次,Socket采用TCP、UDP等協議,這些協議屬于低層次的通信協議;URL采用HTTP和HTTPS這些屬于高層次的通信協議。低層次網絡編程,因為它面向底層,比較復雜,但是“低層次網絡編程”并不等于它功能不強大。恰恰相反,正因為層次低,Socket編程與基于URL的高層次網絡編程比較,能夠提供更強大的功能和更靈活的控制,但是要更復雜一些。 ## 24.1 網絡基礎 **1 客戶端服務器結構網絡** 客戶端服務器(Client Server,縮寫C/S)結構網絡,是一種主從結構網絡。如圖24-1所示,服務器一般處于等待狀態,如果有客戶端請求,服務器響應請求建立連接提供服務。服務器是被動的,有點像在餐廳吃飯時候的服務員。而客戶端是主動的,像在餐廳吃飯的顧客 ![](https://box.kancloud.cn/8e5d2dcf152ffb19e141ef2927c28e89_925x586.png) 例如:Web服務、文件傳輸服務和郵件服務等。雖然它們存在的目的不一樣,但基本結構是一樣的。這種網絡結構與設備類型無關,服務器不一定是電腦,也可能是手機等移動設備。 **2 對等結構網絡** 對等結構網絡也叫點對點網絡(Peer to Peer,縮寫P2P),每個節點之間是對等的。它們如圖24-2所示,每個節點既是服務器又是客戶端,這種結構有點像吃自助餐。 ![](https://box.kancloud.cn/723209bd11bb7d004377fa85c17af00f_868x552.png) 對等結構網絡分布范圍比較小。通常在一間辦公室或一個家庭內,因此它非常適合于移動設備間的網絡通訊,網絡鏈路層是由藍牙和WiFi實現。 ### 24.1.2 TCP/IP協議 網絡通信會用到協議,其中TCP/IP協議是非常重要的。TCP/IP協議是由IP和TCP兩個協議構成的,IP(Internet Protocol)協議是一種低級的路由協議,它將數據拆分成許多小的數據包中,并通過網絡將它們發送到某一特定地址,但無法保證都所有包都抵達目的地,也不能保證包的順序。 由于IP協議傳輸數據的不安全性,網絡通信時還需要TCP協議,傳輸控制協議(Transmission Control Protocol,TCP)是一種高層次的協議,面向連接的可靠數據傳輸協議,如果有些數據包沒有收到會重發,并對數據包內容準確性檢查并保證數據包順序,所以該協議保證數據包能夠安全地按照發送時順序送達目的地。 ### 24.1.3 IP地址 為實現網絡中不同計算機之間的通信,每臺計算機都必須有一個與眾不同的標識,這就是IP地址,TCP/IP使用IP地址來標識源地址和目的地址。最初所有的IP地址都是32位數字構成,由4個8位的二進制數組成,每8位之間用圓點隔開,如:192.168.1.1,這種類型的地址通過IPv4指定。 而現在有一種新的地址模式稱為IPv6,IPv6使用128位數字表示一個地址,分為8個16位塊。盡管IPv6比IPv4有很多優勢,但是由于習慣的問題,很多設備還是采用IPv4。不過Java語言同時指出IPv4和IPv6。 在IPv4地址模式中IP地址分為A、B、C、D和E等5類。 * A類地址用于大型網絡,地址范圍:1.0.0.1~126.155.255.254。 * B類地址用于中型網絡,地址范圍:128.0.0.1~191.255.255.254。 * C類地址用于小規模網絡,192.0.0.1~223.255.255.254。 * D類地址用于多目的地信息的傳輸和作為備用。 * E類地址保留僅作實驗和開發用。 另外,有時還會用到一個特殊的IP地址127.0.0.1,127.0.0.1稱為回送地址,指本機。主要用于網絡軟件測試以及本地機進程間通信,使用回送地址發送數據,不進行任何網絡傳輸,只在本機進程間通信。 ### 24.1.4 端口 一個IP地址標識這一臺計算機,每一臺計算機又有很多網絡通信程序在運行,提供網絡服務或進行通信,這就需要不同的端口進行通信。如果把IP地址比作電話號碼,那么端口就是分機號碼,進行網絡通信時不僅要指定IP地址,還要指定端口號。 TCP/IP系統中的端口號是一個16位的數字,它的范圍是0~65535。小于1024的端口號保留給預定義的服務,如HTTP是80,FTP是21,Telnet是23,Email是25等,除非要和那些服務進行通信,否則不應該使用小于1024的端口。 ## 24.2 TCP Socket低層次網絡編程 TCP/IP協議的傳輸層有兩種傳輸協議:TCP(傳輸控制協議)和 UDP(用戶數據報協議)。TCP是面向連接的可靠數據傳輸協議。TCP就像好比電話,電話接通后雙方才能通話,在掛斷電話之前,電話一直占線。TCP連接一旦建立起來,一直占用,直到關閉連接。另外,TCP為了保證數據的正確性,會重發一切沒有收到的數據,還會對進行數據內容進行驗證,并保證數據傳輸的正確順序。因此TCP協議對系統資源的要求較多。 基于TCP Socket編程很有代表性,先介紹TCP Socket編程。 ### 24.2.1 TCP Socket通信概述 Socket是網絡上的兩個程序,通過一個雙向的通信連接,實現數據的交換。這個雙向鏈路的一端稱為一個Socket。Socket通常用來實現客戶端和服務端的連接。Socket是TCP/IP協議的一個十分流行的編程接口,一個Socket由一個IP地址和一個端口號唯一確定,一旦建立連接Socket還會包含本機和遠程主機的IP地址和遠端口號,如圖24-3所示,Socket是成對出現的。 ![](https://box.kancloud.cn/842255068c15a53b11812d0790d4d7b5_935x346.png) ### 24.2.2 TCP Socket通信過程 使用Socket進行C/S結構編程,通信過程如圖24-4所示。 ![](https://box.kancloud.cn/f034b7d87bf7ccc53a9b49d88aa3d69a_796x743.png) ![](https://box.kancloud.cn/1a1ac48211c6b45714eabc04b6ee524c_906x715.png) **圖24-4 TCP Socket通信過程** 服務器端監聽某個端口是否有連接請求,服務器端程序處于阻塞狀態,直到客戶端向服務器端發出連接請求,服務器端接收客戶端請求,服務器會響應請求,處理請求,然后將結果應答給客戶端,這樣就會建立連接。一旦連接建立起來,通過Socket可以獲得輸入輸出流對象。借助于輸入輸出流對象就可以實現服務器與客戶端的通信,最后不要忘記關閉Socket和釋放一些資源(包括:關閉輸入輸出流)。 ### 24.2.3 Socket類 java.net包為TCP Socket編程提供了兩個核心類:Socket和ServerSocket,分別用來表示雙向連接的客戶端和服務器端。 **本節先介紹一下Socket類,Socket常用的構造方法有:** * Socket(InetAddress address, int port) :創建Socket對象,并指定遠程主機IP地址和端口號。 * Socket(InetAddress address, int port, InetAddress localAddr, int localPort):創建Socket對象,并指定遠程主機IP地址和端口號,以及本機的IP地址(localAddr)和端口號(localPort)。 * Socket(String host, int port):創建Socket對象,并指定遠程主機名和端口號,IP地址為null,null表示回送地址,即127.0.0.1。 * Socket(String host, int port, InetAddress localAddr, int localPort):創建Socket對象,并指定遠程主機和端口號,以及本機的IP地址(localAddr)和端口號(localPort)。host主機名,IP地址為null,null表示回送地址,即127.0.0.1。 **Socket其他的常用方法有:** * InputStream getInputStream():通過此Socket返回輸入流對象。 * OutputStream getOutputStream():通過此Socket返回輸出流對象。 * int getPort():返回Socket連接到的遠程端口。 * int getLocalPort():返回Socket綁定到的本地端口。 * InetAddress getInetAddress():返回Socket連接的地址。 * InetAddress getLocalAddress():返回Socket綁定的本地地址。 * boolean isClosed():返回Socket是否處于關閉狀態。 * boolean isConnected():返回Socket是否處于連接狀態。 * void close():關閉Socket。 **注意** Socket與流類似所占用的資源,不能通過JVM的垃圾收集器回收,需要程序員釋放。一種方法是可以在finally代碼塊調用close()方法關閉Socket,釋放流所占用的資源。另一種方法通過自動資源管理技術釋放資源,Socket和ServerSocket都實現了AutoCloseable接口。 ### 24.2.4 ServerSocket類 **ServerSocket類常用的構造方法有:** * ServerSocket(int port, int maxQueue):創建綁定到特定端口的服務器Socket。maxQueue設置連接的請求最大隊列長度,如果隊列滿時,則拒絕該連接。默認值是50。 * ServerSocket(int port):創建綁定到特定端口的服務器Socket。最大隊列長度是50。 **ServerSocket其他的常用方法有:** * InputStream getInputStream():通過此Socket返回輸入流對象。 * OutputStream getOutputStream():通過此Socket返回輸出流對象。 * boolean isClosed():返回Socket是否處于關閉狀態。 * Socket accept():偵聽并接收到Socket的連接。此方法在建立連接之前一直阻塞。 * void close():關閉Socket。 ServerSocket類本身不能直接獲得I/O流對象,而是通過accept()方法返回Socket對象,通過Socket對象取得I/O流對象,進行網絡通信。此外,ServerSocket也實現了AutoCloseable接口,通過自動資源管理技術關閉ServerSocket。 ### 24.2.5 案例:文件上傳工具 基于TCP Socket編程比較復雜,先從一個簡單的文件上傳工具案例介紹TCP Socket編程基本流程。上傳過程是一個單向Socket通信過程,如圖24-5所示 * 客戶端通過文件輸入流讀取文件,然后從Socket獲得輸出流寫入數據,寫入數據完成上傳成功,客戶端任務完成。 * 服務器端從Socket獲得輸入流,然后寫入文件輸出流,寫入數據完成上傳成功,服務器端任務完成。 ![](https://box.kancloud.cn/f7374372561b3ff03ddd92a095c2471e_923x343.png) **下面看看案例服務器端UploadServer代碼如下:** ``` ~~~ //UploadServer.java文件 package com.a51work6; … … public class UploadServer { public static void main(String[] args) { System.out.println("服務器端運行..."); try ( // 創建一個ServerSocket監聽8080端口的客戶端請求 ServerSocket server = new ServerSocket(8080); ① // 使用accept()阻塞當前線程,等待客戶端請求 Socket socket = server.accept(); ② // 由Socket獲得輸入流,并創建緩沖輸入流 BufferedInputStream in = new BufferedInputStream(socket.getInputStream()); ③ // 由文件輸出流創建緩沖輸出流 FileOutputStream fiOut = new FileOutputStream("./TestDir/subDir/coco2dxcplus.jpg") BufferedOutputStream out = new BufferedOutputStream(fiOut ); ) {④ // 準備一個緩沖區 byte[] buffer = new byte[1024]; // 首次從Socket讀取數據 int len = in.read(buffer); while (len != -1) { // 寫入數據到文件 out.write(buffer, 0, len); // 再次從Socket讀取數據 len = in.read(buffer); } System.out.println("接收完成!"); } catch (IOException e) { e.printStackTrace(); } } } ~~~ ``` 上述代碼第①行創建ServerSocket對象監聽本機的8080端口,這是當前線程還沒有阻塞,調用代碼第②行的server.accept()才會阻塞當前線程,等待客戶端請求。 > **提示** 由于當前線程是主線程,所以server.accept()會阻塞主線程,阻塞主線程是不明智的,如果是在一個圖形界面的應用程序,阻塞主線程會導致無法進行任何的界面操作,就是常見的“卡”現象,所以最好是把server.accept()語句放到子線程中。 代碼第③行是從socket對象中獲得輸入流對象,代碼第④行是文件輸出流。下面輸入輸出代碼讀者可以參考第22章,這里不再贅述。 **案例客戶端UploadClient代碼如下:** ``` ~~~ //UploadClient.java文件 package com.a51work6; … … public class UploadClient { public static void main(String[] args) { System.out.println("客戶端運行..."); try ( // 向本機的8080端口發出請求 Socket socket = new Socket("127.0.0.1", 8080); ① // 由Socket獲得輸出流,并創建緩沖輸出流 BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());② // 創建文件輸入流 FileInputStream fin = new FileInputStream("./TestDir/coco2dxcplus.jpg"); // 由文件輸入流創建緩沖輸入流 BufferedInputStream in = new BufferedInputStream(fin)) { // 準備一個緩沖區 byte[] buffer = new byte[1024]; // 首次讀取文件 int len = in.read(buffer); while (len != -1) { // 數據寫入Socket out.write(buffer, 0, len); // 再次讀取文件 len = in.read(buffer); } System.out.println("上傳成功!"); } catch (ConnectException e) { ③ System.out.println("服務器未啟動!"); } catch (IOException e) { e.printStackTrace(); } } } ~~~ ``` 上述代碼第①行創建Socket,指定遠程主機的IP地址和端口號。代碼第②行是從socket對象獲得輸出流。代碼第③行是捕獲ConnectException異常,這個異常引起的原因是在代碼第①行向服務器發出請求時,服務器拒絕了客戶端請求,這有兩種可能性:一是服務器沒有啟動,服務器的8080端口沒有打開;二是服務器請求隊列已滿(默認是50個)。 **提示** 案例測試時,先運行服務器,再運行客戶端。測試Socket程序最好打開兩個命令行窗口,通過java指令分別運行服務器程序和客戶端程序,如圖24-6和24-7所示,需要注意當前運行的路徑是Eclipse工程根目錄,需要指定類路徑,命令的-cp .;./bin就是指定類路徑,包括兩個當前路徑:其中點(.)表示當前路徑,./bin表示bin目錄,也可以寫成.\\bin。為什么要指定bin目錄呢?是因為編譯之后的字節碼文件放在此目錄中。另外,如果想在Eclipse中查看多個控制臺信息,如圖24-8所示,在控制臺上面的工具欄中,單擊“選擇控制臺”按鈕實現切換。 ![](https://box.kancloud.cn/16c80c4359f7f43539ab433b49c588ff_969x496.png) ![](https://box.kancloud.cn/ef08a80422036a2ca292bbd73915863f_933x499.png) ![](https://box.kancloud.cn/3001abda471b9b371fd161ed9162358e_954x323.png) ### 24.2.6 案例:聊天工具 第24.2.5節介紹的案例只是單向傳輸的Socket,Socket可以雙向數據傳輸,但是這就有些復雜了,比較有代表性的案例就是聊天工具。 如圖24-9所示是基于TCP Socket聊天工具案例,其中的標準輸入是鍵盤,標準輸出是顯示器的控制臺。首先客戶端通過鍵盤輸入字符串,通過標準輸入流讀取字符串,然后通過Socket獲得輸出流,將字符串寫入輸出流。接著服務器通過Socket獲得輸入流,從輸入流中讀取來自客戶端發送過來的字符串,然后通過標準輸出流輸出到顯示器的控制臺。服務器向客戶端字符串過程類似。 ![](https://box.kancloud.cn/dbf9d4f6ef590eb5972f5d07a63db5b7_942x388.png) **案例服務器端ChatServer代碼如下**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看