詳解Java網絡編程

一、網絡編程

1.1、概述

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

2、Java是 Internet 上的語言,它從語言級上提供瞭對網絡應用程 序的支持,程序員能夠很容易開發常見的網絡應用程序。

3、Java提供的網絡類庫,可以實現無痛的網絡連接,聯網的底層細節被隱藏在 Java 的本機安裝系統裡,由 JVM 進行控制。並 且 Java 實現瞭一個跨平臺的網絡庫,程序員面對的是一個統一的網絡編程環境。

1.2、計算機網絡基礎

1、概念

把分佈在不同地理區域的計算機與專門的外部設備用通信線路互連成一個規模大、功能強的網絡系統,從而使眾多的計算機可以方便地互相傳遞信息、 共享硬件、軟件、數據信息等資源。

計算機網絡是計算機專業必修的一門學科!裡面涉及到計算機之間的通信、網絡安全等方方面面,有時間可以自行去學習,關於更多的網絡基礎知識這裡就不一一介紹瞭。

2、網絡編程的目的

直接或間接地通過網絡協議與其它計算機實現數據交換,進行通訊。

3、網絡編程中有兩個主要的問題

①如何準確地定位網絡上一臺或多臺主機;定位主機上的特定的應用

②找到主機後如何可靠高效地進行數據傳輸

1.3、網絡通信要素概述

1、我們需要知道的是主機間通過網絡進行通信是需要遵循網絡通信協議,是通過IP地址準確定位主機,通過端口號準確定位主機上的應用。

IP地址和端口號

網絡通信協議

2、如何實現網絡中的主機互相通信?

① 通信雙方地址:IP和端口號

② 一定的規則(即:網絡通信協議。有兩套參考模型)

OSI參考模型:模型過於理想化,未能在因特網上進行廣泛推廣。

TCP/IP參考模型(或TCP/IP協議):事實上的國際標準。

3、網絡通信協議(以TCP/IP模型為例)

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

1.4、IP地址和端口號(組合就是網絡套接字)

我們知道IP地址和端口號是通信要素之一,它們可以唯一確定某一臺主機的某個應用,並為主機之間通信提供瞭可能!那麼什麼是IP地址和端口號呢?

1、IP 地址:InetAddress(在Java中使用InetAddress類代表IP)

  • 一的標識 Internet 上的計算機(通信實體)
  • 本地回環地址(hostAddress):127.0.0.1 主機名(hostName):localhost
  • P地址分類方式1:IPV4 和 IPV6
  • IPV4:4個字節組成,4個0-255。大概42億,30億都在北美,亞洲4億。2011年初已 經用盡。以點分十進制表示,如192.168.0.1
  • IPV6:128位(16個字節),寫成8個無符號整數,每個整數用四個十六進制位表示, 數之間用冒號(:)分開,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
  • IP地址分類方式2:公網地址(萬維網使用)和私有地址(局域網使用)。192.168. 開頭的就是私有址址,范圍即為192.168.0.0–192.168.255.255,專門為組織機 構內部使用
  • 特點:不易記憶

2、InetAddress類

Internet上的主機有兩種方式表示地址:

①域名(hostName):www.baidu.com

②IP 地址(hostAddress):14.215.177.38

InetAddress類主要表示IP地址,兩個子類:Inet4Address、Inet6Address

InetAddress 類對象含有一個 Internet 主機地址的域名和IP地址:www.baidu.com 和 14.215.177.38

域名容易記憶,當在連接網絡時輸入一個主機的域名後,域名服務器(DNS) 負責將域名轉化成IP地址,這樣才能和主機建立連接。 ——-域名解析

/*
1 java中使用InetAddress代表IP
2 IP的分類:IPV4和IPV6     萬維網和局域網

3 域名:www.baidu.com www.cnblogs.com

4 本地回路地址:127.0.0.1 對應著:localhost

5 如何實例化InetAddress類的對象,兩個靜態方法:
    InetAddress getByName(String host)
    InetAddress getLocalHost()

6 兩個常用方法
    getHostName()
    getHostAddress()

7 端口號:正在計算機上運行的進程
 */
public class InetAddressTest {
    public static void main(String[] args){
        try {
            // File file = new File("test.txt");
            InetAddress IP1 = InetAddress.getByName("192.168.3.2");
            System.out.println(IP1);

            InetAddress IP2 = InetAddress.getByName("www.baidu.com");
            System.out.println(IP2);
            // 獲取本地IP
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println(localHost);

            // getHostName()
            System.out.println(IP2.getHostName());
            // getHostAddress()
            System.out.println(IP2.getHostAddress());


        } catch(UnknownHostException e){
            e.printStackTrace();
        }
    }
}

3、端口號

  • 端口號就是標識正在計算機上運行的進程(程序)
  • 不同的進程有不同的端口號
  • 被規定為一個 16 位的整數 0~65535。

端口分類:

① 公認端口:0~1023。被預先定義的服務通信占用(如:HTTP占用端口 80,FTP占用端口21,Telnet占用端口23)

② 註冊端口:1024~49151。分配給用戶進程或應用程序。(如:Tomcat占 用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。

③ 動態/私有端口:49152~65535

4、端口號與IP地址的組合得出一個網絡套接字:Socket

1.5、網絡協議

1、網絡通信協議

計算機網絡中實現通信必須有一些約定,即通信協議,對速率、傳輸代碼、代 碼結構、傳輸控制步驟、出錯控制等制定標準。

2、問題:網絡協議太復雜

計算機網絡通信涉及內容很多,比如指定源地址和目標地址,加密解密,壓縮 解壓縮,差錯控制,流量控制,路由控制,如何實現如此復雜的網絡協議呢?

3、通信協議分層的思想

在制定協議時,把復雜成份分解成一些簡單的成份,再將它們復合起來。最常 用的復合方式是層次方式,即同層間可以通信、上一層可以調用下一層,而與 再下一層不發生關系。各層互不影響,利於系統的開發和擴展。

4、TCP/IP協議簇

傳輸層協議中有兩個非常重要的協議:

  • 傳輸控制協議TCP(Transmission Control Protocol)
  • 戶數據報協議UDP(User Datagram Protocol)

TCP/IP 以其兩個主要協議:傳輸控制協議(TCP)和網絡互聯協議(IP)而得 名,實際上是一組協議,包括多個具有不同功能且互為關聯的協議。

IP(Internet Protocol)協議是網絡層的主要協議,支持網間互連的數據通信。

TCP/IP協議模型從更實用的角度出發,形成瞭高效的四層體系結構,即物理鏈路層、IP層、傳輸層和應用層。

5、TCP 和 UDP

TCP協議:

✔ 使用TCP協議前,須先建立TCP連接,形成傳輸數據通道

✔ 傳輸前,采用“三次握手”方式,點對點通信,是可靠的

✔ TCP協議進行通信的兩個應用進程:客戶端、服務端。

✔ 在連接中可進行大數據量的傳輸

✔ 傳輸完畢,需釋放已建立的連接,效率低

UDP協議:

✔ 將數據、源、目的封裝成數據包,不需要建立連接

✔ 每個數據報的大小限制在64K內

✔ 發送不管對方是否準備好,接收方收到也不確認,故是不可靠的

✔ 可以廣播發送

✔ 發送數據結束時無需釋放資源,開銷小,速度快

1.6、三次握手與四次揮手

1、三次握手

第一步,請求端(客戶端)發送一個包含SYN標志的TCP報文,SYN即同步(Synchronize),同步報文會指明客戶端使用的端口以及TCP連接的初始序號;

第二步,服務器在收到客戶端的SYN報文後,將返回一個SYN+ACK的報文,表示客戶端的請求被接受,同時TCP序號被加一,ACK即確認(Acknowledgment)。

第三步,客戶端也返回一個確認報文ACK給服務器端,同樣TCP序列號被加一,到此一個TCP連接完成。

2、四次揮手

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

1.TCP客戶端發送一個FIN,用來關閉客戶到服務器的數據傳送。

2.服務器收到這個FIN,它發回一個ACK,確認序號為收到的序號加1。和SYN一樣,一個FIN將占用一個序號。

3.服務器關閉客戶端的連接,發送一個FIN給客戶端。

4.客戶端發回ACK報文確認,並將確認序號設置為收到序號加1。

二、TCP網絡編程

2.1、Socket介紹

1、利用套接字(Socket)開發網絡應用程序早已被廣泛的采用,以至於成為事實 上的標準。

2、網絡上具有唯一標識的IP地址和端口號組合在一起才能構成唯一能識別的標識符套接字。

3、通信的兩端都要有Socket,是兩臺機器間通信的端點。

4、網絡通信其實就是Socket間的通信。

5、Socket允許程序把網絡連接當成一個流,數據在兩個Socket間通過IO傳輸。

6、一般主動發起通信的應用程序屬客戶端,等待通信請求的為服務端。

7、Socket分類:

  • 流套接字(stream socket):使用TCP提供可依賴的字節流服務
  • 數據報套接字(datagram socket):使用UDP提供“盡力而為”的數據報服務

2.2、基於Socket的TCP編程

1、Java語言的基於套接字編程分為服務端編程和客戶端編程,其通信模型如圖所示

2、客戶端Socket的工作過程包含以下四個基本的步驟:

  • 創建 Socket:根據指定服務端的 IP 地址或端口號構造 Socket 類對象。若服務器端響應,則建立客戶端到服務端的通信路線。若連接失敗,則會出現異常。
  • 打開連接到 Socket 的輸入/出流: 使用 getInputStream()方法獲得輸入流,使用 getOutputStream()方法獲得輸出流,進行數據傳輸
  • 按照一定的協議對 Socket 進行讀/寫操作:通過輸入流讀取服務器放入線路的信息(但不能讀取自己放入路線的信息),通過輸出流將信息寫入線程
  • 關閉 Socket:斷開客戶端到服務器的連接,釋放線路

3、客戶端創建Socket對象:

客戶端程序可以使用Socket類創建對象,創建的同時會自動向服務器方發起連 接。Socket的構造器是:

// 構造器一
Socket(String host,int port)throws UnknownHostException,IOException
/* 向服務器(域名是 host。端口號為port)發起TCP連接,若成功,則創建Socket對象,否則拋出異常。*/

// 構造器二
Socket(InetAddress address,int port)throws IOException
/* 根據InetAddress對象所表示的 IP地址以及端口號port發起連接。*/

客戶端建立socketAtClient對象的過程就是向服務器發出套接字連接請,簡要步驟如下

Socket s = new Socket("192.168.40.165",9999); // 1、創建Socket對象,指明服務端的IP和端口號
OutputStream out = s.getOutputStream(); // 2、獲取一個輸出流,用於輸出數據
out.write("hello".getBytes()); // 3、寫出數據
s.close(); // 4、回收資源

4、服務器(服務端)程序的工作過程包含以下四個基本的步驟:

  • 調用 ServerSocket(int port) :創建一個服務器端套接字,並綁定到指定端口 上。用於監聽客戶端的請求。
  • 調用 accept():監聽連接請求,如果客戶端請求連接,則接受連接,返回通信 套接字對象。
  • 調用 該Socket類對象的 getOutputStream() 和 getInputStream ():獲取輸出 流和輸入流,開始網絡數據的發送和接收。
  • 關閉ServerSocket和Socket對象:客戶端訪問結束,關閉通信套接字。

5、服務器建立 ServerSocket 對象

ServerSocket 對象負責等待客戶端請求建立套接字連接,類似郵局某個窗口 中的業務員。也就是說,服務器必須事先建立一個等待客戶請求建立套接字 連接的ServerSocket對象。

所謂“接收”客戶的套接字請求,就是accept()方法會返回一個 Socket 對象

ServerSocket ss = new ServerSocket(9999); // 1、創建服務端的ServerSocket,指明自己的端口號
Socket s = ss.accept (); // 2、調用accept()監聽來自客戶端的連接
InputStream in = s.getInputStream(); // 3、獲取輸入流,讀取輸入流的數據
byte[] buf = new byte[1024]; 
int num = in.read(buf); 
String str = new String(buf,0,num); 
System.out.println(s.getInetAddress().toString()+":"+str); 
s.close(); // 4、回收資源
ss.close();

2.3、TCP編程簡單C/S通信示例

/**
 * @description: TCP編程,模擬基於C/S架構客戶端與服務端間的通信
 */
public class SocketTest {
    /* 客戶端 */
    @Test
    public void client(){
        OutputStream output = null;
        Socket socket = null;
        try {
            InetAddress localHost = InetAddress.getByName("127.0.0.1");
            socket = new Socket(localHost,8848);
            output = socket.getOutputStream();
            output.write("hello I'm the client".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(output != null){
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /* 服務端 */
    @Test
    public void server() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream input = null;
        ByteArrayOutputStream out = null;
        try {
            serverSocket = new ServerSocket(8848);
            socket = serverSocket.accept();
            System.out.println("client IP: " + socket.getInetAddress());
            input = socket.getInputStream();
            /*
            一般不建議這樣書寫,數據傳輸時可能會出現亂碼!!
            byte[] buffer = new byte[1024];
            int len;
            while((len = input.read(buffer)) != -1){
                String data = new String(buffer,0,len);
                System.out.println(data);
            }*/

            out = new ByteArrayOutputStream();
            byte[] buffer = new byte[10];
            int len;
            while((len = input.read(buffer)) != -1){
                out.write(buffer,0,len);
            }
            System.out.println(out.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(input != null){
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(serverSocket != null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.4、TCP編程實現C/S文件傳輸

實現功能:客戶端發送文件給服務端,服務端將文件保存在本地。

/**
 * @description: TCP編程,客戶端發送文件給服務端,服務端將文件保存在本地。
 */
public class TCPSocketTest {
	/* 客戶端 */
    @Test
    public void client() {
        Socket socket = null;
        OutputStream writer = null;
        BufferedInputStream bis = null;
        try {
            socket = new Socket(InetAddress.getByName("127.0.0.1"),8089);
            writer = socket.getOutputStream();

            bis = new BufferedInputStream(new FileInputStream(new File("me.jpg")));
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer)) != -1){
                writer.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bis != null){
                try {
                    bis.close();
                    System.out.println("發送成功!");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
	/* 服務端 */
    @Test
    public void server() throws IOException { // 這裡異常應該使用try-catch-finally
        ServerSocket socket = new ServerSocket(8089);
        System.out.println("正在等待客戶端連接...");
        Socket clientSocket = socket.accept();

        System.out.println("客戶端已連接IP地址為:"+clientSocket.getInetAddress().getHostName());
        InputStream is = clientSocket.getInputStream();
        BufferedOutputStream reader = new BufferedOutputStream(new FileOutputStream(new File("new_me.jpg")));

        byte[] buffer = new byte[1024];
        int len;
        while((len = is.read(buffer)) != -1){
            reader.write(buffer,0,len);
        }
        System.out.println("接收成功");

        socket.close();
        clientSocket.close();
        is.close();
        reader.close();
    }
}

2.5、TCP編程實現C/S信息反饋

實現功能:從客戶端發送文件給服務端,服務端保存到本地。並返回“發送成功”給 客戶端。並關閉相應的連接。

/**
 * @description: TCP編程,從客戶端發送文件給服務端,服務端保存到本地。並返回“發送成功”給 客戶端。並關閉相應的連接。
 */
public class TCPSocketTest2 {
	/* 客戶端 */
    @Test
    public void client() {
        Socket socket = null;
        OutputStream writer = null;
        BufferedInputStream bis = null;
        try {
            socket = new Socket(InetAddress.getByName("127.0.0.1"),8089);
            writer = socket.getOutputStream();

            bis = new BufferedInputStream(new FileInputStream(new File("me.jpg")));
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer)) != -1){
                writer.write(buffer,0,len);
            }
            // 關閉數據的輸出
            socket.shutdownOutput();

            // 接收服務端反饋的信息並輸出到控制臺
            InputStream is = socket.getInputStream();
            ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
            byte[] buf = new byte[10];
            int l;
            while((l = is.read(buf)) != -1){
                byteArray.write(buf,0,l);
            }
            System.out.println(byteArray.toString());
            is.close();
            byteArray.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bis != null){
                try {
                    bis.close();
                    System.out.println("發送成功!");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
	/* 服務端 */
    @Test
    public void server() throws IOException { // 這裡異常應該使用try-catch-finally
        ServerSocket socket = new ServerSocket(8089);
        System.out.println("正在等待客戶端連接...");
        Socket clientSocket = socket.accept();

        System.out.println("客戶端已連接IP地址為:"+clientSocket.getInetAddress().getHostName());
        InputStream is = clientSocket.getInputStream();
        BufferedOutputStream reader = new BufferedOutputStream(new FileOutputStream(new File("new_me.jpg")));

        byte[] buffer = new byte[1024];
        int len;
        while((len = is.read(buffer)) != -1){
            reader.write(buffer,0,len);
        }
        System.out.println("接收成功");

        // 服務端給客戶端反饋信息
        OutputStream os = clientSocket.getOutputStream();
        os.write("你好客戶端,照片已經收到".getBytes());

        socket.close();
        clientSocket.close();
        is.close();
        reader.close();
        os.close();
    }
}

三、UDP網絡編程

3.1、UDP網絡通信

1、類 DatagramSocket 和 DatagramPacket 實現瞭基於 UDP 協議網絡程序。

2、UDP數據報通過數據報套接字 DatagramSocket 發送和接收,系統不保證 UDP數據報一定能夠安全送到目的地,也不能確定什麼時候可以抵達。

3、DatagramPacket 對象封裝瞭UDP數據報,在數據報中包含瞭發送端的IP地址和端口號以及接收端的IP地址和端口號。

4、UDP協議中每個數據報都給出瞭完整的地址信息,因此無須建立發送方和接收方的連接。如同發快遞包裹一樣。

3.2、UDP網絡通信流程

1、DatagramSocket與DatagramPacket

2、建立發送端,接收端

3、建立數據包

4、調用Socket的發送、接收方法

5、關閉Socket

註意:發送端與接收端是兩個獨立的運行程序

3.3、UDP網絡通信代碼實現

/*
UDP網絡編程:
    ✔ 將數據、源、目的封裝成數據包,不需要建立連接
    ✔ 每個數據報的大小限制在64K內
    ✔ 發送不管對方是否準備好,接收方收到也不確認,故是不可靠的
    ✔ 可以廣播發送
    ✔ 發送數據結束時無需釋放資源,開銷小,速度快
 */
public class UDPSocketTest {
    @Test // 發送端
    public void send() throws IOException {
        DatagramSocket socket = new DatagramSocket();

        byte[] data = "hello world".getBytes();
        DatagramPacket packet = new DatagramPacket(data,0,data.length, InetAddress.getLocalHost(),8080);

        socket.send(packet);

        socket.close();
    }

    @Test // 接收端
    public void receiver() throws IOException {
        DatagramSocket socket = new DatagramSocket(8080);

        byte[] buffer = new byte[100];
        DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);

        socket.receive(packet);

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

        socket.close();
    }
}

四、URL網絡編程

4.1、URL介紹

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

2、它是一種具體的URI,即URL可以用來標識一個資源,而且還指明瞭如何locate 這個資源。

3、通過 URL 我們可以訪問 Internet 上的各種網絡資源,比如最常見的 www,ftp 站點。瀏覽器通過解析給定的 URL 可以在網絡上查找相應的文件或其他資源。

4、URL的基本結構由5部分組成: <傳輸協議>://<主機名>:<端口號>/<文件名>#片段名?參數列表

  • 例如: http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123
  • #片段名:即錨點,例如看小說,直接定位到章節
  • 參數列表格式:參數名=參數值&參數名=參數值…

5、Restful風格

一種軟件架構風格、設計風格,而不是標準,隻是提供瞭一組設計原則和約束條件。它主要用於客戶端和服務器交互類的軟件。基於這個風格設計的軟件可以更簡潔,更有層次,更易於實現緩存等機制。restful風格在實際開發中使用較多,對於URL地址有全新的使用方式,可以自行瞭解restful風格的使用!

4.2、URL類與類的構造器

1、為瞭表示URL,java.net 中實現瞭類 URL。我們可以通過下面的構造器來初 始化一個 URL 對象:

  • public URL (String spec):通過一個表示URL地址的字符串可以構造一個URL對象。例如:URL url = new URL (“https://www.baidu.com/”);
  • public URL(URL context, String spec):通過基 URL 和相對 URL 構造一個 URL 對象。 例如:URL downloadUrl = new URL(url, “download.html”);
  • public URL(String protocol, String host, String file); 例如:new URL(“http”, “www.atguigu.com”, “download. html”);
  • public URL(String protocol, String host, int port, String file); 例如: URL gamelan = new URL(“http”, “www.atguigu.com”, 80, “download.html”);

2、URL類的構造器都聲明拋出非運行時異常,必須要對這一異常進行處理,通 常是用 try-catch 語句進行捕。

4.3、URL類常用方法

一個URL對象生成後,其屬性是不能被改變的,但可以通過它給定的方法來獲取這些屬性:

  • public String getProtocol( ) 獲取該URL的協議名
  • public String getHost( ) 獲取該URL的主機名
  • public String getPort( ) 獲取該URL的端口號
  • public String getPath( ) 獲取該URL的文件路徑
  • public String getFile( ) 獲取該URL的文件名
  • public String getQuery( ) 獲取該URL的查詢名

以上就是詳解Java網絡編程的詳細內容,更多關於Java 網絡編程的資料請關註WalkonNet其它相關文章!

推薦閱讀: