天天看點

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

首先理清一個概念:網絡程式設計不等于網站程式設計,網絡程式設計即使用套接字來達到程序間通信,現在一般稱為TCP/IP程式設計。

一、計算機網絡

(一)概念

計算機網絡是指将地理位置不同的具有獨立功能的多台計算機及其外部裝置,通過通信線路連接配接起來,在網絡作業系統,網絡管理軟體及網絡通信協定的管理和協調下,實作資源共享和資訊傳遞的計算機系統。

(二)計算機網絡的主要功能

  1. 資源共享:

    資源共享包括計算機硬體資源、軟體資源和資料資源的共享。硬體資源的共享提高了計算機硬體資源的使用率,由于受經濟和其他因素的制約,這些硬體資派不可能所有使用者都有,是以使用計算機網絡不僅可以使用自身的硬體資源,也可共享網絡上的資源。軟體資源和資料資源的共享可以充分利用已有的資訊資派.減少軟體開發過程中的勞動,避免大型資料庫的重複建設。

  2. 資訊交換:

    這是計算機網絡最基本的功能.計算機網絡中的計算機之間或計算機與終端之間,可以快速可靠地互相傳遞資料、程式或檔案。例如,使用者可以在網上傳送電子郵件、交換資料,可以實作在商業部門或公司之間進行訂單、發票等商業檔案安全準确地交換。

  3. 均衡負荷與分布處理:

    對于大型的任務或課題.如果都集中在一台計算機上.負荷太重,這時可以将任務分散到不同的計算機分别完成,或由網絡中比較空閑的計算機分擔負荷,各個計算機連成網絡有利于共同協作進行重大科研課題的開發和研究。利用網絡技術還可以将許多小型機或傲型機連成具有高性能的分布式計算機系統,使它具有解決複雜問題的能力,進而大大降低費用。

  4. 綜合資訊服務:

    計算機網絡可以向全社會提供各處經濟資訊、科研情報、商業資訊和咨詢服務,如Internet中的www就是如此。

(三)計算機網絡分類

計算機網絡按其覆寫的地理範圍可分為如下3類:

  • 區域網路(LAN)。區域網路是一種在小區域内使用的,由多台計算機組成的網絡,覆寫範圍通常局限在10 千米範圍之内,屬于一個機關或部門組建的小範圍網。
  • 城域網(MAN)。城域網是作用範圍在廣域網與區域網路之間的網絡,其網絡覆寫範圍通常可以延伸到整個城市,借助通信光纖将多個區域網路聯通公用城市網絡形成大型網絡,使得不僅區域網路内的資源可以共享,區域網路之間的資源也可以共享。
  • 廣域網(WAN) 廣城網是一種遠端網,涉及長距離的通信,覆寫範圍可以是個國家或多個國家,甚至整個世界。由于廣域網地理上的距離可以超過幾千千米,是以資訊衰減非常嚴重,這種網絡一般要租用專線,通過接口資訊處理協定和線路連接配接起來,構成網狀結構,解決尋徑問題。

二、網絡通信協定及接口

(一)網絡通信協定

計算機網絡中實作通信必須有一些約定,即通信協定;包括對速率,傳輸代碼,代碼結構,傳輸控制步驟,出錯控制等制定的标準。常見的網絡通信協定有:TCP/IP協定、IPX/SPX協定、NetBEUI協定等。

TCP/IP協定:傳輸控制協定/網際網路互聯協定(Transmission Control Protocol/Internet Protocal),是Internet最基本、最廣泛的協定。它定義了計算機如何連入網際網路,以及資料如何在它們之間傳輸的标準。它的内部包含一系列的用于處理資料通信的協定,并采用了4層的分層模型,每一層都呼叫它的下一層所提供的協定來完成自己的需求。

(二)網絡通信接口

為了使兩個節點之間能進行對話,必須在他們之間建立通信工具(即接口),使彼此之間,能進行資訊交換。接口包括兩部分:

  • 硬體裝置:實作結點之間的資訊傳送
  • 軟體裝置:規定雙方進行通信的約定協定

(三)通信協定分層思想

1.為什麼要分層

由于結點之間聯系很複雜,在制定協定時,把複雜成份分解成一些簡單的成份,再将它們複合起來。最常用的複合方式就是層次方式,及同層間可以通信,上一層可以調用下一層,而與再下一層不發生關系。各層互不影響,利于系統的開發和擴充。

2.通信協定的分層規定

把使用者應用程式作為最高層,把實體通信線路作為最底層,将其間的協定處理分為若幹層,規定每層處理的任務,也規定每層的接口标準。

3.參考模型

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

上圖中,TCP/IP協定中的四層分别是應用層、傳輸層、網絡層和鍊路層,每層分别負責不同的通信功能。

  • 應用層:主要負責應用程式的協定,例如HTTP協定、FTP協定等。
  • 傳輸層:主要使網絡程式進行通信,在進行網絡通信時,可以采用TCP協定,也可以采用UDP協定。
  • 網絡層:網絡層是整個TCP/IP協定的核心,它主要用于将傳輸的資料進行分組,将分組資料發送到目标計算機或者網絡。
  • 資料鍊路層:鍊路層是用于定義實體傳輸通道,通常是對某些網絡連接配接裝置的驅動協定,例如針對光纖、網線提供的驅動。

我們編寫的程式位于應用層,是以我們的程式是和TCP/UDP打交道的。

(四)協定分類

通信的協定還是比較複雜的,

java.net

包中包含的類和接口,它們提供低層次的通信細節,我們可以直接使用這些類和接口,來專注于網絡程式開發,而不用考慮通信的細節。

java.net

包中提供了兩種常見的網絡協定的支援:TCP和UDP

  • TCP是可靠的連接配接,TCP就像打電話,需要先打通對方電話,等待對方有回應後才會跟對方繼續說話,也就是一定要确認可以發資訊以後才會把資訊發出去。TCP上傳任何東西都是可靠的,隻要兩台機器上建立起了連接配接,在本機上發送的資料就一定能傳到對方的機器上。
  • UDP就好比發電報,發出去就完事了,對方有沒有接收到它都不管,是以UDP是不可靠的。
  • TCP傳送資料雖然可靠,但傳送得比較慢;UDP傳送資料不可靠,但是傳送得快。

1.UDP

  • 使用者資料報協定(User Datagram Protocol)。
  • 資料報(Datagram):網絡傳輸的基本機關
  • UDP是無連接配接通信協定,即在資料傳輸時,資料的發送端和接收端不建立邏輯連接配接。簡單來說,當一台計算機向另外一台計算機發送資料時,發送端不會确認接收端是否存在,就會發出資料,同樣接收端在收到資料時,也不會向發送端回報是否收到資料。
  • 由于使用UDP協定消耗資源小,通信效率高,是以通常都會用于音頻、視訊和普通資料的傳輸例如視訊會議都使用UDP協定,因為這種情況即使偶爾丢失一兩個資料包,也不會對接收結果産生太大影響。
  • 但是在使用UDP協定傳送資料時,由于UDP的面向無連接配接性,不能保證資料的完整性,是以在傳輸重要資料時,不建議使用UDP協定。
  • 特點:資料被限制在64kb以内,超出這個範圍就不能發送了。

2.TCP

  • 傳輸控制協定(Transmission Control Protocol)。
  • TCP協定是面向連接配接的通信協定,即傳輸資料之前,在發送端和接收端建立邏輯連接配接,然後再傳輸資料,它提供了兩台計算機之間可靠無差錯的資料傳輸。
  • 在TCP連接配接中必須要明确用戶端與伺服器端,由用戶端向服務端發出連接配接請求,每次連接配接的建立都需要經過“三次握手”。

(1)三次握手

TCP協定中,在發送資料的準備階段,用戶端與伺服器之間的三次互動,以保證連接配接的可靠。

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL
  • 第一次握手,用戶端發送SYN(SEQ=x)封包給伺服器端,進入SYN_SEND狀态。

    (用戶端向伺服器端發出連接配接請求,等待伺服器确認。)

  • 第二次握手,伺服器端收到SYN封包,回應一個SYN (SEQ=y)ACK(ACK=x+1)封包,進入SYN_RECV狀态。(伺服器端向用戶端回送一個響應,通知用戶端收到了連接配接請求。)
  • 第三次握手,用戶端收到伺服器端的SYN封包,回應一個ACK(ACK=y+1)封包,進入Established狀态。(用戶端再次向伺服器端發送确認資訊,确認連接配接。)

三次握手完成,TCP用戶端和伺服器端成功地建立連接配接,可以開始傳輸資料了。

(2)四次揮手

其次,TCP的用戶端和服務端斷開連接配接,需要四次揮手

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL
  • 用戶端打算關閉連接配接,此時會發送一個 TCP 首部 FIN 标志位被置為 1 的封包,也即 FIN 封包,之後用戶端進入 FIN_WAIT_1 狀态。
  • 服務端收到該封包後,就向用戶端發送 ACK 應答封包,接着服務端進入 CLOSED_WAIT 狀态。
  • 用戶端收到服務端的 ACK 應答封包後,之後進入 FIN_WAIT_2 狀态。
  • 等待服務端處理完資料後,也向用戶端發送 FIN 封包,之後服務端進入 LAST_ACK 狀态。
  • 用戶端收到服務端的 FIN 封包後,回一個 ACK 應答封包,之後進入 TIME_WAIT 狀态
  • 伺服器收到了 ACK 應答封包後,就進入了 CLOSE 狀态,至此服務端已經完成連接配接的關閉。
  • 用戶端在經過 2MSL 一段時間後,自動進入 CLOSE 狀态,至此用戶端也完成連接配接的關閉。

你可以看到,每個方向都需要一個 FIN 和一個 ACK,是以通常被稱為四次揮手。這裡一點需要注意是:主動關閉連接配接的,才有 TIME_WAIT 狀态。

為什麼揮手需要四次?

關閉連接配接時,用戶端向服務端發送 FIN 時,僅僅表示用戶端不再發送資料了但是還能接收資料。伺服器收到用戶端的 FIN 封包時,先回一個 ACK 應答封包,而服務端可能還有資料需要處理和發送,等服務端不再發送資料時,才發送 FIN 封包給用戶端來表示同意現在關閉連接配接。從上面過程可知,服務端通常需要等待完成資料的發送和處理,是以服務端的 ACK 和 FIN 一般都會分開發送,進而比三次握手導緻多了一次。參考

通俗了解:

  • 三次握手

    A:我要過來了!B:我知道你要過來了!A:我現在過來!

  • 四次揮手

    A:我們分手吧!B:真的分手嗎?B:真的真的要分手嗎?A:是的!

由此,可以可靠地進行連接配接和斷開。

三、IP協定

(一)概念

IP協定:網絡互連協定

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

每個人的電腦都有一個獨一無二的IP位址,這樣互相通信時就不會傳錯資訊了。

(二)分類

IP位址根據版本可以分類為:IPv4和IPv6

IPv4 IPv6
位址長度 IPv4協定具有32位(4位元組)位址長度 IPv6協定具有128位(16位元組)位址長度
格式 IPv4 位址的文本格式為 nnn.nnn.nnn.nnn,其中 0<=nnn<=255,而每個 n 都是十進制數。可省略前導零。 IPv6 位址的文本格式為 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx,其中每個 x 都是十六進制數可省略前導零。
數量 共有43億,30億在北美,4億在亞洲,2011年就已經用盡 多到每一粒沙子都可以配置設定一個IPv6位址
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

IPv4又可以分為五類:

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL
  • A類:在IP位址的四段号碼中,第一段号碼為網絡号碼,剩下的三段号碼為本地計算機的号碼;A類IP位址中網絡的辨別長度為8位,主機辨別的長度為24位,A類網絡位址數量較少,有126個網絡,每個網絡可以容納主機數達1600多萬(256的三次方-2)台。
  • B類:在IP位址的四段号碼中,前兩段号碼為網絡号碼。B類IP位址中網絡的辨別長度為16位,主機辨別的長度為16位,B類網絡位址适用于中等規模的網絡,有16384個網絡,每個網絡所能容納的計算機數為6萬多(256的二次方-2)台
  • C類:在IP位址的四段号碼中,前三段号碼為網絡号碼,剩下的一段号碼為本地計算機的号碼;此類位址中網絡的辨別長度為24位,主機辨別的長度為8位,C類網絡位址數量較多,有209萬餘個網絡。适用于小規模的區域網路絡,每個網絡最多隻能包含254(256-2)台計算機
  • D類:此類IP位址在曆史上被叫做多點傳播位址(multicast address),即多點傳播位址;在以太網中,多點傳播位址命名了一組應該在這個網絡中應用接收到一個分組的站點;多點傳播位址的最高位必須是“1110”,範圍從224.0.0.0到239.255.255.255
  • E類: 此類位址也不分網絡位址和主機位址,它的第1個位元組的前五位固定為“11110”,為将來使用保留,位址範圍從240.0.0.1到255.255.255.254

(三)InetAddress類

說到IP位址,就要引入一個類:InetAddress

此類表示網際網路協定 (IP) 位址。

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

InetAddress類無構造方法

1.常用方法摘要

byte[] getAddress()

傳回此 InetAddress 對象的原始 IP 位址。

static InetAddress getByName(String host)

在給定主機名的情況下确定主機的 IP 位址。

String getHostAddress()

傳回 IP 位址字元串(以文本表現形式)。

String getHostName()

擷取此 IP 位址的主機名。

static InetAddress getLocalHost()

傳回本地主機。

127.0.0.1:本機位址,主要用于測試。别名:Localhost

案例示範1

import java.net.InetAddress;
import java.net.UnknownHostException;

public class TestIP {
    public static void main(String[] args) throws UnknownHostException {
        //InetAdress類表示IP位址

        //擷取本機IP
        InetAddress ip = InetAddress.getLocalHost();// ADMINISTRATOR/192.xxx.xxx.xxx
        System.out.println(ip);
        //獲得主機名
        System.out.println(ip.getHostName());// ADMINISTRATOR
        //獲得IP位址
        System.out.println(ip.getHostAddress());// 192.xxx.xxx.xxx
        //getLocalHost=getHostName+getHostAddress
        System.out.println(InetAddress.getByName("localhost"));
    }
}
           

案例示範2

import java.net.InetAddress;
import java.net.UnknownHostException;

public class TestIP2 {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
        // 擷取此 IP 位址的主機名。
        System.out.println(inetAddress.getHostName());
        //傳回 IP 位址字元串(以文本表現形式)。
        System.out.println(inetAddress.getHostAddress());
    }
}
           
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

四、端口

  • IP位址用來辨別一台計算機,但是一台計算機上可能提供多種網絡應用程式,如何來區分這些不同的程式呢?這就要用到端口。端口号是用來區分一台機器上不同的應用程式的。
  • 我們使用IP位址加端口号,就可以保證資料準确無誤地發送到對方計算機的指定軟體上了。
  • 端口是虛拟的概念,是一個邏輯端口。
  • 當我們使用網絡軟體一打開,那麼作業系統就會為網絡軟體配置設定一個随機的端口号,或者打開的時候向系統要指定的端口号。
  • 通過端口,可以在一個主機上運作多個網絡應用程式。端口的表示是一個16位的二進制整數,2個位元組,對應十進制的0~65535。
  • 端口号在計算機内部是占2個位元組。一台機器上最多有65536個端口号。一個應用程式可以占用多個端口号。端口号如果被一個應用程式占用了,那麼其他的應用程式就無法再使用這個端口号了。
  • 記住一點,我們編寫的程式要占用端口号的話占用1024以上的端口号,1024以下的端口号不要去占用,因為系統有可能會随時征用。端口号本身又分為TCP端口和UDP端口,TCP的8888端口和UDP的8888端口是完全不同的兩個端口。TCP端口和UDP端口都有65536個

(一)分類

  1. 公有端口:0~1023
    • HTTP:80
    • HTTPS:443
    • FTP:21
    • Telnet:23
  2. 程式注冊端口(配置設定給使用者或者程式):1024~49151
    • Tomcat:8080
    • MySQL:3306
    • Oracle:1521
  3. 動态、私有端口:49152~65535

(二)DOS指令檢視端口

  • 檢視所有端口:

    netstat -ano

  • 檢視指定端口:

    netstat -ano|findstr "端口号"

  • 檢視指定端口的程序:

    tasklist|findstr "端口号"

(三)InetSocketAddress類

說到端口,則要引入一個類:InetSocketAddress

此類實作 IP 套接字位址(IP 位址 + 端口号)。

1.構造方法摘要

InetSocketAddress(InetAddress addr, int port)

根據 IP 位址和端口号建立套接字位址。

InetSocketAddress(int port)

建立套接字位址,其中 IP 位址為通配符位址,端口号為指定值。

InetSocketAddress(String hostname, int port)

根據主機名和端口号建立套接字位址。

2.常用方法摘要

InetAddress getAddress()

擷取 InetAddress。

String getHostName()

擷取 hostname。

int getPort()

擷取端口号。

案例示範

import java.net.InetAddress;
import java.net.InetSocketAddress;

public class TestPort {
    public static void main(String[] args) {
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",8082);
        System.out.println(inetSocketAddress);
        //傳回主機名
        System.out.println(inetSocketAddress.getHostName());
        //獲得InetSocketAddress的端口
        System.out.println(inetSocketAddress.getPort());
        //傳回一個InetAddress對象(IP對象)
        InetAddress address = inetSocketAddress.getAddress();
        System.out.println(address);
    }
}
           
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

五、TCP網絡程式設計和UDP網絡程式設計

網絡程式設計也叫做Socket程式設計,即套接字程式設計。套接字指的是兩台裝置之間通訊的端點。

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

(一)TCP網絡程式設計

1.概述

TCP通信能實作兩台計算機之間的資料互動,通信的兩端,要嚴格區分為用戶端(Client)與服務端(Server)。

(1)兩端通信時步驟:

  1. 服務端程式,需要事先啟動,等待用戶端的連接配接。
  2. 用戶端主動連接配接伺服器端,連接配接成功才能通信。服務端不可以主動連接配接用戶端。

(2)在Java中,提供了兩個類用于實作TCP通信程式:

  1. 用戶端:

    java.net.Socket

    類表示。建立

    Socket

    對象,向服務端發出連接配接請求,服務端響應請求,兩者建立連接配接開始通信。
  2. 服務端:

    java.net.ServerSocket

    類表示。建立

    ServerSocket

    對象,相當于開啟一個服務,并等待用戶端的連接配接。

2.Socket類

Socket

類:該類實作用戶端套接字,套接字指的是兩台裝置之間通訊的端點。

(1)構造方法摘要

  • public Socket(String host, int port)

    :建立套接字對象并将其連接配接到指定主機上的指定端口号。如果指定的host是null ,則相當于指定位址為回送位址。

    回送位址(127.x.x.x) 是本機回送位址(Loopback Address),主要用于網絡軟體測試以及本地機程序間通信,無論什麼程式,一旦使用回送位址發送資料,立即傳回,不進行任何網絡傳輸。

案例示範

(2)常用方法摘要

  • public InputStream getInputStream()

    : 傳回此套接字的輸入流。
    • 如果此Scoket具有相關聯的通道,則生成的InputStream 的所有操作也關聯該通道。
    • 關閉生成的InputStream也将關閉相關的Socket。
  • public OutputStream getOutputStream()

    : 傳回此套接字的輸出流。
    • 如果此Socket具有相關聯的通道,則生成的OutputStream 的所有操作也關聯該通道。
    • 關閉生成的OutputStream也将關閉相關的Socket。
  • public void close()

    :關閉此套接字。
    • 一旦一個socket被關閉,它不可再使用。
    • 關閉此socket也将關閉相關的InputStream和OutputStream 。
  • public void shutdownOutput()

    : 禁用此套接字的輸出流。
    • 任何先前寫出的資料将被發送,随後終止輸出流。

3.ServerSocket類

ServerSocket

類:這個類實作了伺服器套接字,該對象等待通過網絡的請求。

(1)構造方法摘要

  • public ServerSocket(int port)

    :使用該構造方法在建立ServerSocket對象時,就可以将其綁定到一個指定的端口号上,參數port就是端口号。

案例示範

(2)常用方法摘要

  • public Socket accept()

    :偵聽并接受連接配接,傳回一個新的Socket對象,用于和用戶端實作通信。該方法會一直阻塞直到建立連接配接。

4.代碼實作

案例示範1

用戶端:

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args){
        Socket socket = null;
        OutputStream os = null;
        try {
            //1、建立Socket對象,它的第一個參數需要的是服務端的IP,第二個參數是服務端的端口
            InetAddress inet = InetAddress.getByName("127.0.0.1");
            socket = new Socket(inet,8090);
            //2、擷取一個輸出流,用于寫出要發送的資料
            os = socket.getOutputStream();
            //3、寫出資料
            os.write("你好,我是用戶端!".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//4、釋放資源
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           

服務端:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            //1、建立服務端的ServerSocket,指明自己的端口号
            serverSocket = new ServerSocket(8090);
            //2、調用accept接收到來自于用戶端的socket
            socket = serverSocket.accept();//阻塞式監聽,會一直等待用戶端的接入
            //3、擷取socket的輸入流
            is = socket.getInputStream();

//        不建議這樣寫:因為如果我們發送的資料有漢字,用String的方式輸出可能會截取漢字,産生亂碼
//        int len=0;
//        byte[] buffer = new byte[1024];
//        while ((len=is.read(buffer))!=-1){
//            String str = new String(buffer, 0, len);
//            System.out.println(str);
//        }
            
            //4、讀取輸入流中的資料
            //ByteArrayOutputStream的好處是它可以根據資料的大小自動擴充
            baos = new ByteArrayOutputStream();
            int len=0;
            byte[] buffer = new byte[1024];
            while ((len=is.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            System.out.println("收到了來自于用戶端"+socket.getInetAddress().getHostName()
                    +"的消息:"+baos.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//5、關閉資源
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(baos!=null){
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

           
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

伺服器是沒有IO流的,伺服器可以擷取到請求的用戶端對象socket

使用每個用戶端socket中提供的IO流和用戶端進行互動

伺服器使用用戶端的位元組輸入流讀取用戶端發送的資料

伺服器使用用戶端的位元組輸出流給用戶端回寫資料

案例示範2

服務端向用戶端回寫資料

用戶端:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args){
        Socket socket = null;
        OutputStream os = null;
        ByteArrayOutputStream baos=null;
        InputStream is=null;
        try {
            //1、建立Socket對象,它的第一個參數需要的是服務端的IP,第二個參數是服務端的端口
            InetAddress inet = InetAddress.getByName("127.0.0.1");
            socket = new Socket(inet,8888);
            //2、擷取一個輸出流,用于寫出要發送的資料
            os = socket.getOutputStream();
            //3、寫出資料
            os.write("你好,我是用戶端!".getBytes());
            //==========================解析回複==================================
            //4、首先必須通知伺服器,我已經輸出完畢了,不然服務端不知道什麼時候輸出完畢
            //服務端的while循環會一直執行,會阻塞
            socket.shutdownOutput();
            ///5、擷取輸入流,用于讀取服務端回複的資料
            is = socket.getInputStream();
            baos = new ByteArrayOutputStream();
            int len=0;
            byte[] buffer = new byte[1024];
            while ((len=is.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            System.out.println("收到了來自服務端的消息:"+baos.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//6、釋放資源
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (baos!=null){
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           

服務端:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        OutputStream os=null;
        try {
            //1、建立服務端的ServerSocket,指明自己的端口号
            serverSocket = new ServerSocket(8888);
            //2、調用accept接收到來自于用戶端的socket
            socket = serverSocket.accept();//阻塞式監聽,會一直等待用戶端接入
            //3、擷取socket的輸入流
            is = socket.getInputStream();

//        不建議這樣寫:因為如果我們發送的資料有漢字,用String的方式輸出可能會截取漢字,産生亂碼
//        int len=0;
//        byte[] buffer = new byte[1024];
//        while ((len=is.read(buffer))!=-1){
//            String str = new String(buffer, 0, len);
//            System.out.println(str);
//        }

            //4、讀取輸入流中的資料
            //ByteArrayOutputStream的好處是它可以根據資料的大小自動擴充
            baos = new ByteArrayOutputStream();
            int len=0;
            byte[] buffer = new byte[1024];
            while ((len=is.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            System.out.println("收到了來自于用戶端"+socket.getInetAddress().getHostName()
                    +"的消息:"+baos.toString());
            //===========================回複==========================================
            //5、擷取一個輸出流,寫出回複給用戶端
            os = socket.getOutputStream();
            //6、寫出資料
            os.write("你好,我是服務端".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//7、關閉資源
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(baos!=null){
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

案例示範3

上傳檔案:用戶端發送檔案給服務端,服務端将檔案儲存在本地

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

用戶端:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) {
        Socket socket = null;
        FileInputStream fis = null;
        OutputStream os = null;
        try {
            //1、建立Socket對象,它的第一個參數需要的是服務端的IP,第二個參數是服務端的端口
            InetAddress inet = InetAddress.getByName("127.0.0.1");
            socket = new Socket(inet, 8888);
            //2、建立一個檔案輸入流,讀取要上傳的檔案
            fis = new FileInputStream("D:/test/touxiang.jpg");
            //3、擷取一個輸出流,用于寫出要發送的資料
            os = socket.getOutputStream();
            byte[] buffer = new byte[1024];
            int len=0;
            while((len=fis.read(buffer))!=-1){
                //4、寫出資料
                os.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//5、釋放資源
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           

服務端:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        FileOutputStream fos = null;
        InputStream is = null;
        try {
            //1、建立服務端的ServerSocket,指明自己的端口号
            serverSocket = new ServerSocket(8888);
            //2、調用accept接收到來自于用戶端的socket
            socket = serverSocket.accept();//阻塞式監聽,會一直等待用戶端的接入
            //3、建立一個檔案輸出流,用于将讀取到的用戶端上傳的檔案輸出
            fos = new FileOutputStream("touxiang.jpg");
            //4、擷取socket的輸入流
            is = socket.getInputStream();
            byte[] buffer = new byte[1024];
            int len=0;
            while((len=is.read(buffer))!=-1){
                fos.write(buffer,0,len);//5、寫出檔案
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//6、釋放資源
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
           

假設大海裡有許多島嶼,用戶端和服務端相當于其中的兩座島嶼,“用戶端”島嶼生産了一種農作物要運到“服務端”島嶼,是以“用戶端”要知道“服務端”确切的位址(IP),不然就運錯地方了,socket就相當于運輸的船隻,port就相當于“服務端”島嶼的某個港口。

(二)UDP網絡程式設計

從技術意義上來講,隻有TCP才會分Server和Client。對于UDP來說,嚴格意義上,并沒有所謂的Server和Client。

java.net

包給我們提供了兩個類

DatagramSocket

(此類表示用于發送和接收資料報的套接字)和

DatagramPacket

(該類表示資料報的資料包。 )

1.DatagramSocket

(1)構造方法摘要

protected DatagramSocket()

構造資料報套接字并将其綁定到本地主機上的任何可用端口。

protected DatagramSocket(int port)

構造資料報套接字并将其綁定到本地主機上的指定端口。

protected DatagramSocket(int port, InetAddress laddr)

建立一個資料報套接字,綁定到指定的本地位址。

2.DatagramPacket

(1)構造方法摘要

DatagramPacket(byte[] buf, int offset, int length)

構造一個 DatagramPacket用于接收指定長度的資料報包到緩沖區中。

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)

構造用于發送指定長度的資料報包到指定主機的指定端口号上。

(2)常用方法摘要

byte[] getData()

傳回資料報包中的資料。

InetAddress getAddress()

傳回該資料報發送或接收資料報的計算機的IP位址。

int getLength()

傳回要發送的資料的長度或接收到的資料的長度。

3.代碼實作

案例示範1

發送方:

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

public class UDPSender {
    public static void main(String[] args) throws IOException {
        //1、建立一個socket
        DatagramSocket socket = new DatagramSocket();
        InetAddress inet = InetAddress.getLocalHost();
        String msg="你好,很高興認識你!";
        byte[] buffer = msg.getBytes();
        //2、建立一個包(要發送給誰)
        DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length,inet,9090);
        //3、發送包
        socket.send(packet);
        //4、釋放資源
        socket.close();
    }
}
           

接收方:

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

public class UDPReceiver {
    public static void main(String[] args) throws IOException {
        //1、建立一個socket,開放端口
        DatagramSocket socket = new DatagramSocket(9090);
        byte[] buffer = new byte[1024];
        //2、建立一個包接收資料
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        //3、接收資料
        socket.receive(packet);//阻塞式接收
        //将資料包轉換為字元串輸出
        String msg = new String(packet.getData(), 0, packet.getLength());
        System.out.println(msg);
        //4、釋放資源
        socket.close();
    }
}
           
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

注意:

如果是TCP中先啟動用戶端會報錯:

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

而如果是UDP中先啟動發送方不會報錯,但會正常退出。

案例示範2

完成線上咨詢功能,學生和老師線上一對一交流(多線程)

發送方:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class UDPSender implements Runnable{
    //建立一個socket
    DatagramSocket socket=null;
    //建立一個流 用于錄入鍵盤的資料
    BufferedReader bfr=null;
    //發送資料目的地的IP
    private String toIP;
    //發送資料目的地的端口
    private int toPort;

    public UDPSender(String toIP, int toPort) {
        this.toIP = toIP;
        this.toPort = toPort;
        try {
            socket=new DatagramSocket();//建立一個socket
        } catch (SocketException e) {
            e.printStackTrace();
        }
        bfr=new BufferedReader(new InputStreamReader(System.in));//從鍵盤錄入資料到流中
    }

    @Override
    public void run() {
        while (true){//循環發送資料
            try {
                String msg = bfr.readLine();//從流中讀取資料
                byte[] buffer = msg.getBytes();
                InetAddress inet = InetAddress.getByName(toIP);
                DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length, inet, toPort);
                socket.send(packet);
                //如果發送了拜拜,則退出發送
                if(msg.equals("拜拜")){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //釋放資源
        if(socket!=null){
            socket.close();
        }
        if (bfr!=null){
            try {
                bfr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
           

接收方:

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

public class UDPReceiver implements Runnable{
    //建立一個socket
    DatagramSocket socket=null;
    //接收方自己所在的端口
    private int fromPort;
    //資料發送者的姓名
    private String msgFrom;

    public UDPReceiver(int fromPort,String msgFrom) {
        this.fromPort = fromPort;
        this.msgFrom=msgFrom;
        try {
            socket=new DatagramSocket(fromPort);//建立一個socket
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while(true){//循環接收
            try {
                byte[] buffer = new byte[1024 * 8];
                DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
                socket.receive(packet);
                String msg = new String(packet.getData(), 0, packet.getLength());
                System.out.println(msgFrom+":"+msg);
                if (msg.equals("拜拜")){//如果接收到的資料為拜拜,則退出接收
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //釋放資源
        socket.close();
    }
}
           

學生線程:

public class Student {
    public static void main(String[] args) {
        new Thread(new UDPSender("127.0.0.1",8888)).start();
        new Thread(new UDPReceiver(7777,"老師")).start();
    }
}
           

老師線程:

public class Teacher {
    public static void main(String[] args) {
        new Thread(new UDPSender("127.0.0.1",7777)).start();
        new Thread(new UDPReceiver(8888,"學生")).start();
    }
}
           

學生的視窗:

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

老師的視窗:

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

六、URL

(一)概念

URL(Uniform Resource Locator):統一資源定位符,它表示Internet上某一資源的位址。

通過URL我們可以通路Internet上的各種網絡資源,比如最常見的www,ftp站點。浏覽器通過解析給定的URL可以在網絡上查找相應的檔案或其他資源。

URI=URL+URN

  • URI:Uniform Resource Identifier ,統一資源标志符。
  • URL:Uniform Resource Locator,統一資源定位符。
  • URN:Uniform Resource Name,統一資源命名。

網絡三大基石:HTML,HTTP,URL

(二)格式

URL的基本結構由5部分組成:

<傳輸協定>://<主機名>:<端口号>/<檔案名>#片段名?參數清單

  • 片段名:即錨點,例如看小說,直接定位到章節
  • 參數清單格式:

    參數名=參數值&參數名=參數值......

例如:

http://localhost:8080/index.jsp#a?username=Tom&password=123456

(三)URL類

java.net

包下

1.構造方法摘要

URL(String spec)

根據 String 表示形式建立 URL 對象。

URL(String protocol, String host, int port, String file)

根據指定協定名、主機名、端口号和檔案名建立 URL 對象。

URL(String protocol, String host, String file)

根據指定的協定名、主機名和檔案名建立 URL。

2.常用方法摘要

String getProtocol()

擷取此 URL的協定名稱。

String getHost()

擷取此 URL 的主機名。

int getPort()

擷取此 URL 的端口号。

String getPath()

擷取此 URL 的檔案路徑。

String getFile()

擷取此 URL 的檔案名。

String getQuery()

擷取此 URL的查詢部分。

URLConnection openConnection()

傳回一個URLConnection執行個體,表示與URL引用的遠端對象的URL 。

  • URLConnection類中又有一個方法:

    InputStream getInputStream()

    傳回從此打開的連接配接讀取的輸入流。

案例示範1

import java.net.MalformedURLException;
import java.net.URL;

public class Test {
    public static void main(String[] args) throws MalformedURLException {
        URL url = new URL("http://localhost:8080/index.jsp?username=Tom&password=123456");
        System.out.println(url.getProtocol());//擷取協定名
        System.out.println(url.getHost());//擷取主機名
        System.out.println(url.getPort());//擷取端口号
        System.out.println(url.getPath());//擷取檔案路徑
        System.out.println(url.getFile());//擷取檔案名
        System.out.println(url.getQuery());//擷取查詢名
    }
}
           
Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL

案例示範2

URL下載下傳網絡資源

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class Test {
    public static void main(String[] args) throws IOException {
        //下載下傳位址
        URL url = new URL("https://img.t.sinajs.cn/t6/style/images/global_nav/WB_logo.png?id=1404211047727");
        //連接配接到這個資源 HTTP
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        InputStream is = urlConnection.getInputStream();
        FileOutputStream fos = new FileOutputStream("weibo.jpg");
        byte[] buffer = new byte[1024];
        int len=0;
        while ((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }
        //釋放資源
        urlConnection.disconnect();//斷開連接配接
        is.close();
        fos.close();
    }
}
           

小結

Java 網絡程式設計一、計算機網絡二、網絡通信協定及接口三、IP協定四、端口五、TCP網絡程式設計和UDP網絡程式設計六、URL