一文入門Java之【網絡編程】


本文設計IO流的知識,若對IO流不瞭解,建議先看一文帶你入門Java之【I/O流】

一、基本概念

網絡編程實質就是計算機之間的數據進行交換,即網絡通信。

1. 什麼是網絡通信

網絡是用物理鏈路將各個孤立的工作站或主機相連在一起,組成數據鏈路,從而達到資源共享和通信的目的。通信是人與人之間通過某種媒體進行的信息交流與傳遞。網絡通信是通過網絡將各個孤立的設備進行連接,通過信息交換實現人與人,人與計算機,計算機與計算機之間的通信。

簡而言之,讓計算機之間可以傳輸數據。

2. 網絡通信的兩大要素

  1. IP和端口號。

  2. 提供網絡通信協議:TCP/IP參考模型(應用層、傳輸層、網絡層、物理+數據鏈路層)。

    1). IP和端口號

    1. IP地址:唯一的標識 Internet 上的計算機(通信實體),Java中用InetAddress類代表IP。
    2. 端口號:在計算機上運行的進程,不同的進程使用不同的端口號,範圍:0-65535
    3. IP地址和端口號組合起來叫做Socket

    2). 網絡通信協議

    a.TCP/UDP

    在這裏插入圖片描述
    TCP和UDP的區別

    TCP協議:

    1. 使用TCP協議前,須先建立TCP連接,形成傳輸數據通道
    2. 傳輸前,採用“三次握手”方式,點對點通信,是可靠的
    3. TCP協議進行通信的兩個應用進程:客戶端、服務端。
    4. 在連接中可進行大數據量的傳輸
    5. 傳輸完畢,需釋放已建立的連接,效率低

    UDP協議:

    1. 將數據、源、目的封裝成數據包,不需要建立連接
    2. 每個數據報的大小限制在64K內
    3. 發送不管對方是否準備好,接收方收到也不確認,故是不可靠的
    4. 可以廣播發送
    5. 發送數據結束時無需釋放資源,開銷小,速度快

    在這裏插入圖片描述
    在這裏插入圖片描述

    b.Socket
    • 利用套接字(Socket)開發網絡應用程序早已被廣泛的採用,以至於成爲事實上的標準。
    • 網絡上具有唯一標識的IP地址和端口號組合在一起才能構成唯一能識別的標識符套接字。
    • 通信的兩端都要有Socket,是兩臺機器間通信的端點。
    • 網絡通信其實就是Socket間的通信。
    • Socket允許程序把網絡連接當成一個流,數據在兩個Socket間通過IO傳輸。
    • 一般主動發起通信的應用程序屬客戶端,等待通信請求的爲服務端。
    • Socket分類:
      流套接字(stream socket):使用TCP提供可依賴的字節流服務
      數據報套接字(datagram socket):使用UDP提供“盡力而爲”的數據報服務

    Socket類的常用構造器:

    • public Socket(InetAddress address,int port)創建一個流套接字並將其連接到指定IP地址的指定端口號。
    • public Socket(String host,int port)創建一個流套接字並將其連接到指定主機上的指定端口號。
    • Socket類的常用方法:
      public InputStream getInputStream()返回此套接字的輸入流。可以用於接收網絡消息
      public OutputStream getOutputStream()返回此套接字的輸出流。可以用於發送網絡消息
      public InetAddress getInetAddress()此套接字連接到的遠程IP地址;如果套接字是未連接的,則返回null。
      public InetAddress getLocalAddress()獲取套接字綁定的本地地址。即本端的IP地址
      public int getPort()此套接字連接到的遠程端口號;如果尚未連接套接字,則返回0。
      public int getLocalPort()返回此套接字綁定到的本地端口。如果尚未綁定套接字,則返回-1。即本端的端口號。
      public void close()關閉此套接字。套接字被關閉後,便不可在以後的網絡連接中使用(即無法重新連接或重新綁定)。需要創建新的套接字對象。關閉此套接字也將會關閉該套接字的InputStream和OutputStream。
      public void shutdownInput()如果在套接字上調用shutdownInput()後從套接字輸入流讀取內容,則流將返回EOF(文件結束符)。即不能在從此套接字的輸入流中接收任何數據。
      public void shutdownOutput()禁用此套接字的輸出流。對於TCP套接字,任何以前寫入的數據都將被髮送,並且後跟TCP的正常連接終止序列。如果在套接字上調用shutdownOutput()後寫入套接字輸出流,則該流將拋出IOException。即不能通過此套接字的輸出流發送任何數據。

二、InetAddress類的使用

1.實例化

InetAddress.getByName(ip地址/域名/主機);

2. 常用方法

  1. getHostName(); 獲取此IP地址的主機名
  2. getHostAddress(); 以文本形式返回IP地址字符串:原始IP地址以字符串格式返回

 public static void main(String[] args) throws UnknownHostException {

        // 實例化  InetAddress.getByName(); 給定主機名,確定主機的IP地址。
        InetAddress name = InetAddress.getByName("192.168.0.1");
        System.out.println(name);

        InetAddress name1 = InetAddress.getByName("www.baidu.com");
        System.out.println(name1);

        InetAddress name2 = InetAddress.getByName("localhost");
        System.out.println(name2);

        // 實例化  InetAddress.getLocalHost();  獲取本地IP
        InetAddress ip = InetAddress.getLocalHost();
        System.out.println("獲取本地IP:" + ip);

        //常用方法

        //getHostName();  獲取此IP地址的主機名
        System.out.println("獲取此IP地址的主機名:" + name2.getHostName());

        //getHostAddress(); 以文本形式返回IP地址字符串:原始IP地址以字符串格式返回
        System.out.println("以文本形式返回IP地址字符串:" + name2.getHostAddress());
    }

三、TCP網絡通信

基於TCP的Socket通信基於TCP的Socket通信

1.客戶端Socket的工作過程包含以下四個基本的步驟

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

2. 服務器程序的工作過程包含以下四個基本的步驟

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

3 .代碼示例

要求:從客戶端發送文件給服務端,服務端保存到本地。並返回“圖片接收成功!”給客戶端。

先啓動服務端,後啓動客戶端

import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPTest {
    //客戶端
    @Test
    public void client() throws IOException {
        //1. 創建Socket對象,指定服務器的ip地址和端口號
        InetAddress inet = InetAddress.getByName("127.0.0.1");
        Socket socket = new Socket(inet,4321);
        //2. 獲取一個輸出流,用於傳輸數據
        OutputStream os = socket.getOutputStream();
        //3. 獲取文件輸入流
        FileInputStream fileInputStream = new FileInputStream(new File("img1.jpg"));
        //4. 寫出數據操作
        byte[] buffer = new byte[1024];
        int len;
        while((len = fileInputStream.read(buffer)) != -1){
            os.write(buffer,0,len);
        }
        //5.關閉輸出數據流
        socket.shutdownOutput();

        //6. 接收來自於服務器端的數據
        InputStream inputStream = socket.getInputStream();
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        byte[] buffer1 = new byte[5];
        int len1;
        while ((len1 = inputStream.read(buffer1)) != -1){
            byteStream.write(buffer1,0,len1);
        }
        //7. 輸出數據
        System.out.println(byteStream);
        //8. 資源關閉
        byteStream.close();
        inputStream.close();
        os.close();
        socket.close();
    }

    //服務端
    @Test
    public void server() throws IOException {
        // 1. 創建服務端的ServerSocket
        ServerSocket serverSocket = new ServerSocket(4321);
        //2. 調用accept()表示接收客戶端的socket
        Socket socket = serverSocket.accept();
        //3. 獲取輸入流
        InputStream inputStream = socket.getInputStream();
        //4. 獲取一個文件輸出流
        FileOutputStream fileOutputStream = new FileOutputStream(new File("tcpimg.jpg"));
        //5. 讀取輸入流中的數據

        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1){
            fileOutputStream.write(buffer,0,len);
        }
        // 服務端提示
        System.out.println("圖片傳送成功");
        //6.向 客戶端發送 消息
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("圖片接收成功!".getBytes());

        // 6. 關閉資源
        outputStream.close();
        fileOutputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

四、UDP網絡通信

  • 類DatagramSocket和DatagramPacket實現了基於UDP 協議網絡程序。
  • UDP數據報通過數據報套接字DatagramSocket發送和接收,系統不保證UDP數據報一定能夠安全送到目的地,也不能確定什麼時候可以抵達。
  • DatagramPacket 對象封裝了UDP數據報,在數據報中包含了發送端的IP地址和端口號以及接收端的IP地址和端口號。
  • UDP協議中每個數據報都給出了完整的地址信息,因此無須建立發送方和接收方的連接。如同發快遞包裹一樣。

1.DatagramSocket 類的常用方法

public DatagramSocket(int port)創建數據報套接字並將其綁定到本地主機上的指定端口。套接字將被綁定到通配符地址,IP地址由內核來選擇
public DatagramSocket(int port,InetAddress laddr)創建數據報套接字,將其綁定到指定的本地地址。本地端口必須在0到65535之間(包括兩者)。如果IP地址爲0.0.0.0,套接字將被綁定到通配符地址,IP地址由內核選擇。
public void close()關閉此數據報套接字。
public void send(DatagramPacket p)從此套接字發送數據報包。DatagramPacket包含的信息指示:將要發送的數據、其長度、遠程主機的IP地址和遠程主機的端口號。
public void receive(DatagramPacket p)從此套接字接收數據報包。當此方法返回時,DatagramPacket的緩衝區填充了接收的數據。數據報包也包含發送方的IP地址和發送方機器上的端口號。此方法在接收到數據報前一直阻塞。數據報包對象的length字段包含所接收信息的長度。如果信息比包的長度長,該信息將被截短。
public InetAddress getLocalAddress()獲取套接字綁定的本地地址。
public int getLocalPort()返回此套接字綁定的本地主機上的端口號。
public lnetAddress getInetAddress()返回此套接字連接的地址。如果套接字未連接,則返回null。
public int getPort()返回此套接字的端口。如果套接字未連接,則返回-1。

2.DatagramPacket類的常用方法

public DatagramPacket(byte[] buf,int length)構造DatagramPacket,用來接收長度爲length的數據包。length參數必須小於等於buf.length。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port)構造數據報包,用來將長度爲length的包發送到指定主機上的指定端口號。length參數必須小於等於buf.length。
public InetAddress getAddress()返回某臺機器的IP地址,此數據報將要發往該機器或者是從該機器接收到的。
public int getPort()返回某臺遠程主機的端口號,此數據報將要發往該主機或者是從該主機接收到的。
public byte[] getData()返回數據緩衝區。接收到的或將要發送的數據從緩衝區中的偏移量offset處開始,持續length長度。
public int getLength()返回將要發送或接收到的數據的長度。

3. 發送/接收數據步驟

  1. 創建DatagramSocket與DatagramPacket
  2. 建立發送端/接收端
  3. 建立數據包
  4. 調用Socket的發送/接收方法
  5. 關閉Socket

4. 示例

先運行接收端,後運行發送端。

public class UDPTest {
    //發送端
    @Test
    public void sender() throws IOException {
        //1. 創建DatagramSocket對象
        DatagramSocket socket = new DatagramSocket();
        String str = "Hello,UDP!";
        byte[] data = str.getBytes();
        //2. 建立客戶端
        InetAddress inet = InetAddress.getLocalHost();
        //3. 建立數據包
        DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
        //4. 調用發送方法
        socket.send(packet);
        //5.關閉socket
        socket.close();

    }
    //接收端
    @Test
    public void receiver() throws IOException {
        //1. 創建DatagramSocket對象
        DatagramSocket socket = new DatagramSocket(9090);

        //2.接收數據包
        byte[] buffer = new byte[100];
        DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
        //3. 調用接收方法
        socket.receive(packet);

        System.out.println(new String(packet.getData(),0,packet.getLength()));
        //4. 關閉socket
        socket.close();
    }
}

五、URL編程

1. URL類

  • URL(Uniform Resource Locator):統一資源定位符,它表示Internet 上某一資源的地址。
  • 它是一種具體的URI,即URL可以用來標識一個資源,而且還指明瞭如何locate這個資源。
  • 通過URL 我們可以訪問Internet 上的各種網絡資源,比如最常見的www,ftp 站點。瀏覽器通過解析給定的URL 可以在網絡上查找相應的文件或其他資源。
  • URL的基本結構由5部分組成:<傳輸協議>://<主機名>:<端口號>/<文件名>#片段名?參數列表
  • 例:http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123
  • #片段名:即錨點,例如看小說,直接定位到章節參數列表格式:參數名=參數值&參數名=參數值…
  • 爲了表示URL,java.net 中實現了類URL。我們可以通過下面的構造器來初始化一個URL 對象:
  • public URL (String spec):通過一個表示URL地址的字符串可以構造一個URL對象。例如:URL url= new URL (“http://www. atguigu.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, intport, String file); 例如: URL gamelan = new URL(“http”, “www.atguigu.com”, 80, “download.html");
  • URL類的構造器都聲明拋出非運行時異常,必須要對這一異常進行處理,通常是用try-catch 語句進行捕獲。
  • 一個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的查詢名

 public static void main(String[] args) throws MalformedURLException {
        URL url = new URL("http://www.join-tsinghua.edu.cn/publish/bzw2019/image/logo.png");
        //getProtocol()     獲取該URL的協議名
        System.out.println(url.getProtocol());
        //getHost()獲取該URL的主機名
        System.out.println(url.getHost());
        //getPort()獲取該URL的端口號
        System.out.println(url.getPort());
        //getPath()獲取該URL的文件路徑
        System.out.println(url.getPath());
        //getFile()獲取該URL的文件名
        System.out.println(url.getFile());
        //public String getQuery() 獲取該URL的查詢名
        System.out.println(url.getQuery());
    }

2. URLConnection類

  • URL的方法openStream():能從網絡上讀取數據
  • 若希望輸出數據,例如向服務器端的CGI (公共網關接口-Common Gateway Interface-的簡稱,是用戶瀏覽器和服務器端的應用程序進行連接的接口)程序發送一些數據,則必須先與URL建立連接,然後才能對其進行讀寫,此時需要使用URLConnection。
  • URLConnection:表示到URL所引用的遠程對象的連接。當與一個URL建立連接時,首先要在一個URL 對象上通過方法openConnection() 生成對應的URLConnection對象。如果連接過程失敗,將產生IOException.

URL netchinaren= new URL (“http://www.baidu.com”);
URLConnectonnu = netchinaren.openConnection( );

  • 通過URLConnection對象獲取的輸入流和輸出流,即可以與現有的CGI程序進行交互。

public Object getContent() throws IOException
public intgetContentLength()
public String getContentType()
public long getDate()
public long getLastModified()
public InputStream getInputStream() throws IOException
public OutputSteram getOutputStream() throws IOException

例:下載一張圖片

@Test
    public void testDownload() throws IOException {
        HttpURLConnection urlConnection = null;
        InputStream is = null;
        FileOutputStream fos = null;
        URL url = new URL("http://www.join-tsinghua.edu.cn/publish/bzw2019/image/logo.png");

        urlConnection = (HttpURLConnection)url.openConnection();

        urlConnection.connect();

        is = urlConnection.getInputStream();
        fos = new FileOutputStream("logo.png");

        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
        }

        System.out.println("下載完成");
        is.close();
        fos.close();
        urlConnection.disconnect();
    }

3. URI、URL和URN的區別

在這裏插入圖片描述
URI,是uniform resource identifier,統一資源標識符,用來唯一的標識一個資源。而URL是uniform resource locator,統一資源定位符,它是一種具體的URI,即URL可以用來標識一個資源,而且還指明瞭如何locate這個資源。而URN,uniform resource name,統一資源命名,是通過名字來標識資源,比如mailto:[email protected]。也就是說,URI是以一種抽象的,高層次概念定義統一資源標識,而URL和URN則是具體的資源標識的方式。URL和URN都是一種URI。在Java的URI中,一個URI實例可以代表絕對的,也可以是相對的,只要它符合URI的語法規則。而URL類則不僅符合語義,還包含了定位該資源的信息,因此它不能是相對的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章