1. 程式人生 > 其它 >14Java進階網路程式設計API

14Java進階網路程式設計API

1.網路協議的三要素:語義、語法和時序

語義表示要做什麼,語法表示要怎麼做,時序表示做的順序。

2.網路OSI七層模型

OSI/RM 模型(Open System Interconnection/Reference Model)。它將計算機網路體系結構的通訊協議劃分為七層,自下而上依次為物理層(Physics Layer)、資料鏈路層(Data Link Layer)、網路層(Network Layer)、傳輸層(Transport Layer)、會話層(Session Layer)、表示層(Presentation Layer)、應用層(Application Layer)。

3.TCP四層模型簡介

自下而上依次為網路介面層(Network Interface Layer)、網路層(Network Layer)、傳輸層(Transport Layer)和應用層(Application Layer)。

還可以轉換為五層模型,即將網路介面層分為物理層和資料鏈路層。

3.1TCP四層作用

物理層規定了物理介質的各種特性,包括機械特性、電子特性、功能特性和規程特性。

資料鏈路層負責接收資料幀並通過網路傳送,或從網路上接收物理幀再抽離出資料幀交給網路層。

網路層管理網路中的資料通訊,設法將資料從源端經過若干中間節點傳送到目的端,從而向傳輸層提供最基本的端到端的資料傳送服務。

傳輸層主要功能包括分割和重組資料並提供差錯控制和流量控制,以達到提供可靠傳輸的目的。為了實現可靠的傳輸,傳輸層協議規定接收端必須傳送確認資訊以確定資料達到,假如資料丟失,必須重新發送。

應用層對應於 OSI 七層模型的會話層、表示層和應用層,該層向用戶提供一組常用的應用程式服務。

3.2傳輸層的常用協議

1.傳輸控制協議(Transmission Control Protocol,TCP),是一種可靠的面向連線的傳輸服務協議。在 TCP/IP 協議族中,TCP 提供可靠的連線服務,採用“三次握手”建立一個連線。

2.使用者資料報協議(User Datagram Protocol,UDP),是另外一個重要的協議,它提供的是無連線、面向事務的簡單不可靠資訊傳送服務。UDP 不提供分割、重組資料和對資料進行排序的功能,也就是說,當資料傳送之後,無法得知其是否能安全完整地到達。

UDP是無連線的、不可靠的,資源消耗小,處理速度快,但是在網路不好的情況下丟包比較嚴重。

3.3應用層的常用協議

  1. 檔案傳輸協議(File Transfer Protocol,FTP),上傳、下載檔案可以使用 FTP 服務。

  2. Telnet 協議,提供使用者遠端登入的服務,使用明碼傳送,保密性差,但簡單方便。

  3. 域名解析服務(Domain Name Service,DNS),提供域名和 IP 地址之間的解析轉換。

  4. 簡單郵件傳輸協議(Simple Mail Transfer Protocol,SMTP),用來控制郵件的傳送、中轉。

  5. 超文字傳輸協議(Hypertext Transfer Protocol,HTTP),用於實現網際網路中的 WWW 服務。

  6. 郵局協議的第三個版本(Post Office Protocol 3,POP3),它是規定個人計算機如何連線到網際網路上的郵件伺服器進行收發郵件的協議。

3.4資料的封裝與解封

從上向下,資料的傳輸需要加上相應的頭部和尾部,稱為資料的封裝。

從下向上,資料的傳輸需要去掉相應的頭部和尾部,稱為資料的解封。

4 IP地址及其表示

IP地址由兩部分組成:網路號和主機號

網路號表示該地址處於哪一個網路,主機號表示該地址的主機。

IP 地址有兩種表示方式,二進位制表示和點分十進位制表示,常見的是點分十進位制表示的 IP 地址。IP 地址的長度為 32 位,每 8 位組成一個部分,一個 IP 地址可以分為四個部分。如果每個部分用十進位制表示,其值的範圍為 0 ~ 255,不同部分之間用“.”分割開來。

5 域名簡介及其分類

域名可分為不同級別,包括頂級域名、二級域名等。

頂級域名又可分為以下兩類:

一類是國家頂級域名,200 多個國家都按照 ISO3166 國家程式碼分配了頂級域名,例如中國是 cn,美國是 us,韓國是 kr 等。

另一類是國際頂級域名,一般表示著註冊企業類別的符號,例如表示工商企業的 com,表示網路提供商的 net,表示非營利組織的 org 等。

二級域名是指頂級域名之下的域名,例如在國際頂級域名下,由域名註冊人申請註冊的網上名稱,例如 sohu、apple、microsoft 等。

6 InetAddress——獲取IP地址

  • InetAddress[] getAllByName(String host):通過主機名和配置名返回IP地址。

  • InetAddress getByAddress(byte[] addr):通過 IP 地址陣列返回 InetAddress 物件。

  • InetAddress getByAddress(String host, byte[] addr):根據提供的主機名 host 和位元組陣列形式的 IP 地址 addr,建立 InetAddress 物件。

  • InetAddress getByName(String host):給定主機名 host,返回 InetAddress 物件。

  • InetAddress getLocalHost():返回本地主機 InetAddress 物件。

InetAddress 類的其他常用方法有以下幾種:

  • byte[] getAddress():返回此 InetAddress 物件的 IP 地址陣列。

  • String getCanonicalHostName():返回此 IP 地址的完全限定域名。完全限定域名是指主機名加上全路徑,全路徑中列出了序列中所有域成員。

  • String getHostAddress():返回 IP 地址字串。

  • String getHostName():返回此 IP 地址的主機名。

7 URL類——獲取網路資源的位置

構造方法:URL(地址)

獲取頁面的輸入位元組流:url.openStream()

使用IO方法對頁面的內容進行獲取和解析

8 URLConnection類——連線通訊

1.使用url.openConnection()獲取連線物件。

2.設定引數和一般請求屬性。

3.使用connect()方法進行遠端連線。

8.1 URLConnection的具體屬性

  • boolean doInput :將 doInput 標誌設定為 true,指示應用程式要從 URL 連線讀取資料,此屬性的預設值為 true。此屬性由 setDoInput() 方法設定,其值由 getDoInput() 方法返回。

  • boolean doOutput :將 doOutput 標誌設定為 true,指示應用程式要將資料寫入 URL 連線,此屬性的預設值為 false。此屬性由 setDoOutput() 方法設定,其值由 getDoOutput() 方法返回。

  • boolean useCaches :如果其值為 true,則只要有條件就允許協議使用快取;如果其值為 false,則該協議始終必須獲得此物件的新副本,其預設值為上一次呼叫 setDefaultUseCaches() 方法時給定的值。此屬性由 setUseCaches() 方法設定,其值由 getUseCaches() 方法返回。

  • boolean connected:表示URL是否連線成功

  • URL url:表示Connection類在網上開啟的url物件。

9 使用Socket程式設計之TCP Socket

Socket:套接字,用於端到端的通訊。

ServerSocket:用於服務端物件的建立。伺服器會初始化一個埠號的Socket,監聽此埠的連線。如果客戶端建立連線,會分配一個帶有新的埠號的Socket,用來和客戶端對話。當連線結束時,會關閉Socket。

ServerSocket的accept()方法:ServerSocket的accept()方法從連線請求佇列中取出一個客戶的連線請求,然後建立與客戶連線的Socket物件,並將它返回。如果佇列中沒有連線請求,accept()方法就會一直等待,直到接收到了連線請求才返回。

伺服器為請求連線的客戶程序建立一個先進先出佇列,預設大小一般是50,如果呼叫accept()方法就會從佇列中取出連線請求,服務端為其建立一個socket。如果佇列已經滿,伺服器會拒絕新的連線請求。

9.1使用Socket建立CS連線

package two;
​
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
​
public class Test7 {
​
}
class TestServer{
    public static void main(String[] args) {
        ServerSocket socket = null;
        try {
            //建立服務端的socket
            socket = new ServerSocket(8888);
            //建立一個socket,接受客戶端的套接字
            Socket s = socket.accept();
            //顯示
            System.out.println("客戶端的IP:"+s.getInetAddress()+",客戶端的埠號:"+s.getPort());
            s.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
class TestClient{
    public static void main(String[] args) {
        try {
            //服務端和客戶端在同一區域網內。服務端的埠號是8888
            Socket socket = new Socket("127.0.0.1",8888);
            //從socket中獲得客戶端的埠號和IP
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            if(dis.available()!=0)System.out.println(dis.readUTF());
            dis.close();
            socket.close();
        }catch (ConnectException e) {
            e.printStackTrace();
            System.err.println("伺服器連線失敗!");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
​
    }
}

9.2使用socket進行圖片上傳

package org.lanqiao.service;
​
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
​
public class UploadService {
    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(10203);
            Socket socket = server.accept();
            //獲取對客戶端寫入的資料輸出位元組流
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            File file = new File("timg.jpg");
            //從本地圖片獲得輸入流
            FileInputStream fis = new FileInputStream(file);
            //寫入輸出流
            dos.write(fis.readAllBytes());
            dos.close();
            fis.close();
            socket.close();
            server.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
package org.lanqiao.client;
​
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
​
public class UploadClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 10203);
            //獲取服務端發來的輸入流
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            File file = new File("pic/mn.jpg");
            FileOutputStream fos = new FileOutputStream(file);
            //將圖片資料寫入本地檔案
            fos.write(dis.readAllBytes());
            System.out.println("上傳完成");
            fos.close();
            dis.close();
            socket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
}

9.3 使用多執行緒優化CS聊天室

package two;
​
import jdk.net.Sockets;
​
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
​
public class Test8 {
    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(8888);
            while (true) {
                //只要服務端開啟,就一直接受客戶端的socket
                //ServerSocket的accept()方法從連線請求佇列中取出一個客戶的連線請求,然後建立與客戶連線的Socket物件,並將它返回。如果佇列中沒有連線請求,accept()方法就會一直等待,直到接收到了連線請求才返回。
                //socket中儲存的是客戶端的ip地址和向客戶端進行通訊的埠號
                Socket socket = server.accept();
                //併為這個socket啟動新的服務端執行緒
                ServerThread st = new ServerThread(socket);
                st.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
​
class ServerThread extends Thread {
    Socket socket;
    Scanner sc = new Scanner(System.in);
​
    public ServerThread(Socket socket) {
        this.socket = socket;
    }
​
    @Override
    public void run() {
        InputStream is = null;
        OutputStream os = null;
        try {
            //獲取這個socket的輸入輸出流
            is = socket.getInputStream();
            os = socket.getOutputStream();
            DataInputStream dis = new DataInputStream(is);
            DataOutputStream dos = new DataOutputStream(os);
            String str = null;
            while (true) {
                //一直通訊,直到客戶端斷開連線丟擲異常
                if ((str = dis.readUTF()) != null) {
                    if (str.equals("e")) break;
                    System.out.println("客戶端發來的訊息:" + str);
​
                }
                System.out.println("請輸入要向客戶端傳送的訊息:");
                String msg = sc.next();
                dos.writeUTF(msg);
                System.out.println();
            }
            dos.close();
            dis.close();
            socket.close();
            //EOFException表示意外到達流的結尾,如流中斷
        } catch (EOFException e) {
            System.out.println("客戶端" + socket.getInetAddress().getHostAddress() + ":" + socket.getPort() + "退出!");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
​
class ClientThread extends Thread {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
​
        try {
            //建立連線,獲取socket
            //socket中儲存的是服務端的ip地址和向服務端進行通訊的埠號
            Socket socket = new Socket("127.0.0.1", 8888);
            InputStream is = socket.getInputStream();
            OutputStream os = socket.getOutputStream();
            DataInputStream dis = new DataInputStream(is);
            DataOutputStream dos = new DataOutputStream(os);
            while (true) {
                //一直通訊,直到使用者輸入退出連線
                if (dis.available() != 0) System.out.println("伺服器發來訊息:" + dis.readUTF());
                System.out.println("請輸入要向伺服器傳送的訊息(傳送e結束):");
                String msg = sc.next();
                if (msg.equals("e")) {
                    System.out.println("已退出聊天");
                    break;
                }
                dos.writeUTF(msg);
            }
            dos.close();
            dis.close();
            os.close();
            is.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

10使用Socket程式設計之UDP Socket

DatagramSocket類:接受和傳送資料報的套接字。使用UDP廣播發送

DatagramPacket類:此類表示資料報包,它用來實現無連線包投遞服務。根據該包中包含的地址和埠等資訊,將報文從一臺機器路由到另一臺機器。

DatagramPacket的建構函式:

DatagramPacket(byte[] buf,int readLength):用於構建接受資訊的資料報包

DatagramPacket(byte[] buf,int readLength,InetAddress inet):用於構建傳送資訊的資料報包,inet物件中需要指定IP和埠號

10.1使用UDP實現客戶端向服務端傳送訊息

解決中文輸入輸出亂碼:在傳送端建立資料報包時,不要直接使用字串或者字串.getBytes()獲得的位元組陣列作為資料。建立ByteArrayOutputStream和以baos為輸出流的DataOutputStream,使用dos的writeUTF()方法,再獲得baos使用的位元組陣列。將這個陣列作為引數。

在接收端建立接受資料包時,不要直接顯示buf建立的字串,先建立以buf為輸入的ByteArrayInputStream和以bais為輸入流的DataInputStream,最後使用dis的readUTF()讀出這個位元組陣列。這樣就會識別中文、韓文等語言。

package two;
​
import java.io.*;
import java.net.*;
import java.util.Scanner;
​
public class Test10 {
}
​
class UDPClient {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        try {
            DatagramSocket ds = new DatagramSocket(9999);
            System.out.println("客戶端:");
            while (true) {
                String line = sc.next();
                if (line.equals("bye")) break;
                //建立baos和dos將讀入資料輸出到位元組陣列中
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(baos);
                dos.writeUTF(line);
                byte[] buf = baos.toByteArray();
                DatagramPacket packet = new DatagramPacket(buf,buf.length, new  InetSocketAddress("127.0.0.1", 8888));
                //datagramsocket.send(datagrampacket)方法傳送資料報
                ds.send(packet);
            }
            ds.close();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
​
class UDPServer {
    public static void main(String[] args) {
        try {
            //接收端socket註冊埠號
            DatagramSocket ds = new DatagramSocket(8888);
            System.out.println("伺服器端:");
            while (true) {
                byte[] buf = new byte[1024];
                //獲取接收端資料報
                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                //datagramsocket.receive(datagrampacket)方法接受資料報
                ds.receive(packet);
                //packet.getData()獲取資料資訊,返回的是位元組陣列,需要根據陣列的長度構建字串            //使用bais和dis讀取獲得的位元組陣列。
                ByteArrayInputStream bais = new ByteArrayInputStream(buf);
                DataInputStream dis = new DataInputStream(bais);
                System.out.println(dis.readUTF());
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}

10.2使用UDP完成檔案上傳

package org.lanqiao.client;
​
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
​
public class Client {
    public static void main(String[] args) {
       File file = new File("text.txt");
        try {
            //讀取檔案資訊到位元組陣列中
            DataInputStream dis = new DataInputStream(new FileInputStream(file));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            System.out.println("客戶端在10203監聽");
            dos.write(dis.readAllBytes());
            byte[] b = baos.toByteArray();
            dos.close();
            //建立UDPSocket和資料報包,傳送位元組陣列  
            DatagramSocket ds = new DatagramSocket(8888);
            DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("127.0.0.1",10203));
            //傳送資料報
            ds.send(dp);
            System.out.println("檔案傳送完成");
            ds.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}
package org.lanqiao.service;
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
​
public class Service {
    public static void main(String[] args) {
        try {
            //建立Socket,註冊為10203埠
            DatagramSocket ds = new DatagramSocket(10203);
            System.out.println("在10203埠監聽...");
            byte[] buf = new byte[10000];
            //建立接受資料報包
            DatagramPacket packet = new DatagramPacket(buf,buf.length);
            ds.receive(packet);
           //資料輸入位元組流讀取資料報中的資料
            ByteArrayInputStream bais = new ByteArrayInputStream(buf);
            DataInputStream dis = new DataInputStream(bais);
            File file = new File("data/text.txt");
            file.createNewFile();
             //通過位元組流寫入檔案
            DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
            dos.write(dis.readAllBytes());
            System.out.println("檔案接收完成");
            dos.close();
            dis.close();
            bais.close();
            ds.close();
        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
}

11網路爬蟲

網路爬蟲:百度、谷歌等引擎從網際網路中爬取資料的方式。

網路爬蟲的注意事項:

  1. 不要大規模的爬取網路資料,或對對方的伺服器造成較大影響。

  2. 不要將爬蟲用於灰色產業、敏感行業,或使用爬蟲非法獲利。

  3. 不要使用爬蟲獲取網站或使用者的隱私資料。

  4. 不要違背 robots 協議或經營者意志。

  5. 不要使用爬蟲進行任何法律、法規或道德禁止的行為。

實現爬蟲:使用URLConnection和給定URL建立連線,獲取連線的輸入流,就是網站所在的網路資源。使用正則表示式對html標籤進行提取,就可以獲得網頁上顯示的資訊。

11.2 使用爬蟲爬取藍橋主頁上指定標籤的元素內容

package two;
​
import org.w3c.dom.ls.LSOutput;
​
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
​
public class Test14 {
    public static void main(String[] args) {
        String html = getOCode();
      //  System.out.println(html);
        String res = parseResource(html);
        System.out.println(res==null?"爬取失敗":res);
    }
​
    public static String getOCode() {
        BufferedReader reader = null;
        StringBuffer html = new StringBuffer();
        try {
            URL url = new URL("https://www.lanqiao.cn/");
            URLConnection connection = url.openConnection();
            connection.connect();
            //開啟連線後,建立字元輸入流讀取頁面原始碼
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                //儲存到StringBuffer中
                html.append(line);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return html.toString();
    }
​
    public static String parseResource(String html) {
        String res = null;
        //Pattern.compile()建立正則表示式物件
        Pattern pattern = Pattern.compile("meta data-n-head=\"ssr\" data-hid=\"description\" name=\"description\" content=\"(.+?)\"");
        //通過pattern.matcher(源字串)獲得匹配的Matcher物件
        Matcher matcher = pattern.matcher(html);
        //如果匹配的字串存在
        if(matcher.find()){
           //matcher.group(0)獲取匹配的整個串
            res = matcher.group(0);
            //獲取所需要的子串部分
            res = res.substring(res.indexOf("content=")+"content=".length());
        }
        return res;
    }
}

爬取結果:
group[0] :meta data-n-head="ssr" data-hid="description" name="description" content="藍橋雲課是國內領先的IT線上程式設計及線上實訓學習平臺,專業導師提供精選的實踐專案,        創新的技術使得學習者無需配置繁瑣的本地環境,隨時線上流暢使用。以就業為導向,        提供程式設計、運維、測試、雲端計算、大資料、資料庫等全面的IT技術動手實踐環境,        提供Linux、Python、Java、C語言、Node.js、Hadoop、PHP、Docker、Git、        R、SQL、MongoDB、Redis、Swift、Spark等千門熱門課程。"
​
​
"藍橋雲課是國內領先的IT線上程式設計及線上實訓學習平臺,專業導師提供精選的實踐專案,        創新的技術使得學習者無需配置繁瑣的本地環境,隨時線上流暢使用。以就業為導向,        提供程式設計、運維、測試、雲端計算、大資料、資料庫等全面的IT技術動手實踐環境,        提供Linux、Python、Java、C語言、Node.js、Hadoop、PHP、Docker、Git、        R、SQL、MongoDB、Redis、Swift、Spark等千門熱門課程。"