網路編程基礎

基礎知識

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

服務端:像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("//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

Tags: