網路程式設計基礎

卡斯特梅的雨傘發表於2021-12-03

基礎知識

客戶端:指瀏覽器或者自定義的客戶端。

服務端:像Tomcat伺服器或者自定義客戶端。

image.png
image.png

image.png
image.png

TCP/IP

TCP:傳輸層協議。

IP:網路層協議。

TCP/IP.png

TCP/UDP

TCP與UDP區別

TCP使用案例:用於RPC介面呼叫,傳送電子郵件等需要可靠性傳輸的事情。

UDP使用案例:用於視訊這類的傳輸。

相同點:

  • TCP與UDP都是網路層的通訊協議,只是通訊的協議不同。

不同點:

  • TCP協議在通訊前要先通過三次握手的方式建立連線,保證通訊的可靠性。而UDP不需要建立連線接可以傳送資料包,因此接收方不一定能收到,因此是不可靠的。
  • TCP協議需要建立連線和釋放連線,因此傳輸效率低。而UDP不需要釋放資源,因此開銷小,速度快。
  • UDP可以進行廣播傳送,而TCP是點對點通訊。
  • TCP在建立連線後可以進行大資料量的傳輸,而UDP會限制每個資料包的傳輸大小在64K以內。

TCP/UDP.png

TCP

socket.png
socket2.png
socket3.png

三次握手

客戶端建立連線請求時的三次握手:

第一次握手:客戶端向服務端傳送syn報文和序號x;

第二次握手:服務端接收到客戶端的請求,傳送ack=x+1報文,併傳送序號y,syn報文。

第三次握手:客戶端接收到服務端的請求,傳送ack= y+1報文,併傳送序號z。

為什麼是三次握手而不是二次或者四次五次呢?

因此根據三次握手我們客戶端和服務端都可以知道自己傳送正常,對方接收正常;而二次握手的話,服務端並不知道自己傳送正常,只能知道客戶端傳送接收正常,自己接收正常,而不知道自己傳送正常。而三次握手剛好就滿足了這個條件。既然三次握手就滿足了,四次五次就會顯得多餘了,雖然更多次的握手可以更加保證通訊的正常,但是正常來說三次握手就能保證通訊99%是可靠的,再多次的握手可靠性的提高並不高,意義不大。

三次握手.png

四次揮手

客戶端服務端都可以通過四次揮手關閉連線,但一般都是客戶端發起四次揮手請求來關閉連線,因為我們服務端一般是24小時線上服務的。

以客戶端發起斷開連線為例:

客戶端告訴服務端我要斷開連線了。

服務端相應客戶端說我收到你的斷開連線請求了。

服務端斷開連線,併傳送請求告訴客戶端我和你斷開連線了。

客戶端收到斷開連線請求斷開了連線,併傳送確認斷開的請求告訴服務我和你斷開連線了,這時候服務端就接收不到客戶端的請求了,如果接收到了就表示沒有真正斷開連線。

四次揮手.png

示例小結

public class TCPTest {
    /**
     * 客戶端
     */
    @Test
    public void client() throws IOException {
        Socket socket = null;
        OutputStream os = null;
        try {
            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
            //1建立Socke連線物件,指明要連線的服務端的ip和埠號
            socket = new Socket(inetAddress, 9000);
            //2獲取輸出流,用於輸出資料
            os = socket.getOutputStream();
            //3寫資料
            os.write("我叫客戶端,你好啊".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4關閉流資源
            if (os != null) {
                os.close();
            }
            if (socket != null) {
                socket.close();
            }
        }
    }

    @Test
    public void server() throws IOException {
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            //建立服務端的ServerSocket,指明自己的埠號,給客戶端通過指定的ip和該埠號確定程式進行連線
            serverSocket = new ServerSocket(9000);
            //accept()用於接收來自客戶端的連線
            socket = serverSocket.accept();
            //獲取輸入流
            is = socket.getInputStream();
            //讀取輸入流中的資料,ByteArrayOutputStream該流維護動態陣列儲存寫入的資料
            baos = new ByteArrayOutputStream();
            byte[] buff = new byte[1024];
            int len;
            while ((len = is.read(buff)) != -1) {
                baos.write(buff, 0, len);
            }
            System.out.println(baos.toString());
            System.out.println("收到客戶端請求了!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //關閉資源
            baos.close();
            is.close();
            socket.close();
            serverSocket.close();
        }
    }
    /**
     * 我叫客戶端,你好啊
     * 收到客戶端請求了!
     */
}

示例小結——TCP傳送接收檔案

public class TCPFileTest {
    /**
     * 客戶端給服務端發生檔案
     */
    @Test
    public void client() throws IOException {
        Socket socket = null;
        OutputStream os = null;
        FileInputStream fis = null;
        ByteArrayOutputStream baos = null;
        try {
            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
            //1建立Socke連線物件,指明要連線的服務端的ip和埠號
            socket = new Socket(inetAddress, 9000);
            //2獲取輸出流,用於輸出資料
            os = socket.getOutputStream();
            fis = new FileInputStream("是大臣.jpg");
            //3寫資料
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            System.out.println("向服務端傳送圖片成功!");
            //關閉資料的輸出,不然服務端會一直阻塞著接收不往下走
            socket.shutdownOutput();
            //接收服務端的應答
            InputStream is = socket.getInputStream();
            baos = new ByteArrayOutputStream();
            byte[] buff = new byte[1024];
            int length;
            while ((length = is.read(buff)) != -1) {
                baos.write(buff, 0, length);
            }
            System.out.println(baos.toString());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4關閉流資源
            if (os != null) {
                os.close();
            }
            if (socket != null) {
                socket.close();
            }
        }
    }
    /**
     * 向服務端傳送圖片成功!
     * 客戶端,你的檔案收到了
     */

    /**
     * 服務端接收檔案儲存到本地
     *
     * @throws IOException
     */
    @Test
    public void server() throws IOException {
        InputStream is = null;
        FileOutputStream fos = null;
        ServerSocket serverSocket = null;
        Socket socket = null;
        OutputStream os = null;
        try {
            //建立服務端的ServerSocket,指明自己的埠號,給客戶端通過指定的ip和該埠號確定程式進行連線
            serverSocket = new ServerSocket(9000);
            //accept()用於接收來自客戶端的連線
            socket = serverSocket.accept();
            //獲取輸入流
            is = socket.getInputStream();
            //開啟輸出流寫檔案
            fos = new FileOutputStream("是大臣-copy5.jpg");
            byte[] buff = new byte[1024];
            int len;
            while ((len = is.read(buff)) != -1) {
                fos.write(buff, 0, len);
            }
            System.out.println("收到客戶端請求了!");
            //給客戶端寫出應答
            os = socket.getOutputStream();
            os.write("客戶端,你的檔案收到了".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //關閉資源
            fos.close();
            is.close();
            socket.close();
            serverSocket.close();
            os.close();
        }
    }
    /**
     * 收到客戶端請求了!
     */
}

UDP

image.png
image.png
image.png
image.png

示例小結

public class UDPTest {

    /**
     * 發生端
     */
    @Test
    public void sender() throws IOException {
        //1建立socket,傳送端不需要指定要發生的ip和埠,直接在要發生的包裡指定接收的地址埠即可。
        DatagramSocket socket = new DatagramSocket();
        byte[] data = "我是傳送端,這是給你的一封信!".getBytes();
        //資料包要傳送的地址
        InetAddress address = InetAddress.getByName("127.0.0.1");
        //2建立傳送包
        DatagramPacket packet = new DatagramPacket(data, 0, data.length, address, 9999);
        //3傳送
        socket.send(packet);
        System.out.println("傳送資料成功!");
        //4關閉流
        socket.close();
    }


    /**
     * 接收端
     */
    @Test
    public void receiver() throws IOException {
        //1建立接收端socket,要指定接收端的埠號,才能接收
        DatagramSocket socket = new DatagramSocket(9999);
        //2建立用於接收資料流的陣列容器
        byte[] buffer = new byte[1024];
        //2建立接收包,用於接收資料包
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        //3傳送
        socket.receive(packet);
        //4列印接收到的資料
        System.out.println(new String(packet.getData(),0,packet.getLength()));
        System.out.println("接收資料成功!");
        //5關閉流
        socket.close();
    }
}

URL

image.png
image.png

示例小結

public class URLTest {

    /**
     * 使用url去下載網路檔案
     *
     * @throws IOException
     */
    @Test
    public void urlTest() throws IOException {
        //URL相當於種子
        URL url = new URL("https://pics4.baidu.com/feed/d009b3de9c82d1586f9efd2871ac9ad1bd3e42bf.jpeg?token=dd219671927c0cb92aebbd8808110a70");
        //根據不同的協議強轉不同的urlConnection,HttpsURLConnection或者HttpURLConnection
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        InputStream is = urlConnection.getInputStream();
        //寫到本地
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("pic.jpeg"));
        byte[] buff = new byte[1024];
        int len;
        while ((len = is.read(buff)) != -1) {
            bos.write(buff, 0, len);
        }
        System.out.println("下載成功了!");
        //關閉流
        is.close();
        bos.close();
        urlConnection.disconnect();
    }
}

參考

Java入門視訊教程

618-630

相關文章