Java基礎—網絡編程(二)

網絡編程(二)

一、TCP協議通信

    1.上傳圖片練習
      1)將圖片上傳到服務器端保存。

示例代碼:

package com.heisejiuhuche.socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.Socket;

public class PicUploadClient {
    public static void main(String[] args) throws Exception {
        upload();
    }

    private static void upload() throws Exception {
        /* 創建Socket對象,指定主機和端口 */
        Socket s = new Socket("localhost", 10007);
        /* 創建File對象,關聯文件 */
        File file = new File("C://Users//jeremy//Documents//javaTmp//me.jpg");
        /* 創建輸入輸出流對象 */
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        int b = 0;
        /* 上傳文件 */
        while((b = bis.read()) != -1) {
            bos.write(b);
            bos.flush();
        }
        /* 發送結束符 */
        s.shutdownOutput();
        /* 獲取反饋信息 */
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int msg = in.read(buf);
        System.out.println(new String(buf, 0, msg));
        bis.close();
        s.close();
    }
}

package com.heisejiuhuche.socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class PicUploadServer {
    public static void main(String[] args) throws Exception {
        recv();
    }

    private static void recv() throws Exception {
        ServerSocket ss = new ServerSocket(10007);
        Socket s = ss.accept();
        /* 創建輸入輸出流對象 */
        BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
        File file = new File("C://Users//jeremy//Documents//me.jpg");
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
        int b = 0;
        /* 保存文件 */
        while((b = bis.read()) != -1) {
            bos.write(b);
            bos.flush();
        }
        /*發送反饋信息 */
        OutputStream out = s.getOutputStream();
        out.write("上傳成功".getBytes());
        bos.close();
        s.close();
        ss.close();
    }
}

程序運行結果:上傳成功

      2)TCP客戶端併發上傳圖片
        這個服務端有侷限性。當一個客戶端連接服務器之後,因爲服務器是單線程,那麼其他客戶端如果想連接服務器,只能帶前一個客戶完成所有任務,才能連接。要同時處理多個客戶端請求,應該明確客戶端要在服務器端要執行的代碼,然後將每個客戶端封裝到一個線程當中,同步執行。

示例代碼:

package com.heisejiuhuche.socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.Socket;

public class UploadPicByThreadClient {
    public static void main(String[] args) throws Exception {
        /* 限制文件格式,大小等條件 */
        if(args.length != 1) {
            System.out.println("請指定一個jpg文件上傳...");
            return;
        }
        File file = new File(args[0]);
        if(!(file.exists() && file.isFile())) {
            System.out.println("文件不存在或者不是文件類型...");
            return;
        }

        if(!(file.getName().endsWith(".jpg"))) {
            System.out.println("格式不正確...");
            return;
        }

        if(file.length() > 1024 * 1024 * 5) {
            System.out.println("文件過大!");
            return;
        }

        /* 創建Socket對象,指定主機和端口 */
        Socket s = new Socket("localhost", 10007);
        /* 創建File對象,關聯文件 */
//      File file = new File("C://Users//jeremy//Documents//javaTmp//me.jpg");
        /* 創建輸入輸出流對象 */
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        int b = 0;
        /* 上傳文件 */
        while((b = bis.read()) != -1) {
            bos.write(b);
            bos.flush();
        }
        /* 發送結束符 */
        s.shutdownOutput();
        /* 獲取反饋信息 */
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int msg = in.read(buf);
        System.out.println(new String(buf, 0, msg));
        bis.close();
        s.close();
    }
}

package com.heisejiuhuche.socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadPicByThreadServer {
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(10007);
        while(true) {
            Socket s = ss.accept();
            new Thread(new PicServerThread(s)).start();
        }
    }
}

/* 將客戶端上傳的代碼封裝成線程 */
class PicServerThread implements Runnable {
    private Socket s;

    PicServerThread(Socket s) {
        this.s = s;
    }

    public void run() {
        String ip = s.getInetAddress().getHostAddress();
        int count = 1;
        try {
            System.out.println(ip + " connected..");
            /* 避免文件名重複 */
            File file = new File("C://Users//jeremy//Documents//" + ip + "(" + count + ")" + ".jpg");
            /* 循環判斷,如果文件存在,讓count自增 */
            while(file.exists())
                file = new File("C://Users//jeremy//Documents//" + ip + "(" + count++ + ")" + ".jpg");
            /* 讀取文件,寫入服務端 */
            BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
            int b = 0;
            while((b = bis.read()) != -1) {
                bos.write(b);
                bos.flush();
            }
            OutputStream out = s.getOutputStream();
            out.write("上傳成功".getBytes());
            bos.close();
            s.close();
        } catch(IOException e) {
            throw new RuntimeException(ip + " 上傳失敗...");
        }
    }
}

    2.檢測用戶名練習
      客戶端通過鍵盤錄入用戶名,如果該用戶存在,在服務器端顯示xxx,已登錄;在客戶端顯示xxx,歡迎光臨。如果用戶不存在,在服務端顯示xxx,嘗試登錄;在客戶端顯示xxx,該用戶不存在。

示例代碼:

package com.heisejiuhuche.socket;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class LoginClient {
    public static void main(String[] args) throws Exception {
        Socket s = new Socket("localhost", 10009);
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
        BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
        for(int x = 0; x < 3; x++) {
            String line = bufr.readLine();
            /* 如果輸入爲空,直接結束循環 */
            if(line == null)
                break;
            /* 發送名字到服務器端 */
            pw.println(line);
            /* 讀取回饋信息 */
            String feedback = bufIn.readLine();
            System.out.println(feedback);
            /* 如果回饋信息有歡迎字樣,結束循環 */
            if(feedback.contains("歡迎"))
                break;
        }
        bufr.close();
        s.close();
    }
}

package com.heisejiuhuche.socket;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class LoginServer {
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(10009);
        while(true) {
            Socket s = ss.accept();
            new Thread(new UserThread(s)).start();
        }
    }
}

class UserThread implements Runnable {
    private Socket s;

    UserThread(Socket s) {
        this.s = s;
    }

    public void run() {
        String ip = s.getInetAddress().getHostAddress();
        System.out.println(ip + " connected...");
        try {
            for(int x = 0; x < 3; x++) {
                BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
                String name = bufIn.readLine();
                if(name == null)
                    break;
                /* 這一行代碼一定放在for循環裏面,不然readLine的指針回不到文件開端,讀不到名字 */
                BufferedReader bufr = new BufferedReader(new FileReader("C://Users//jeremy//Documents//javaTmp//user.txt"));
                PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
                String line = null;
                Boolean flag = false;
                /* 根據flag的結果判斷要進行的操作  */
                while((line = bufr.readLine()) != null) {
                    if(name.equals(line)) {
                        flag = true;
                        break;
                    }
                }
                if(flag) {
                    System.out.println(name + "已登錄!");
                    pw.println(name + ", 歡迎登錄!");
                    break;
                } else {
                    System.out.println(ip + "正在嘗試登錄...");
                    pw.println(name + "不存在...");
                }
                bufr.close();
            }
            s.close();
        } catch(IOException e) {
            throw new RuntimeException(ip + "校驗失敗...");
        }
    }
}

    3.自定義瀏覽器
      1)HTTP請求消息頭
        請求頭信息中包括了發送該請求的主機地址,端口,路徑,可以接受的應用程序類型,所使用語言,封裝壓縮形式等信息。請求頭信息示例如下:

GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:11000
DNT: 1
Connection: Keep-Alive

模擬一個瀏覽器,想服務器發送請求頭信息,獲取頁面。

示例代碼:

package com.heisejiuhuche.socket;

import java.io.InputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class BrowserClient {
    public static void main(String[] args) throws Exception {
        /* 訪問本機的Tomcat服務器,端口8080 */
        Socket s = new Socket("localhost", 8080);
        PrintWriter out = new PrintWriter(s.getOutputStream(), true);
        /* 發送HTTP請求頭 */
        out.println("GET / HTTP/1.1");
        out.println("Accept: */*");
        out.println("Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3");
        out.println("Accept-Encoding: gzip, deflate");
        out.println("Connection: closed");
        /* 用空行分割請求頭和請求體 */
        out.println();
        out.println();
        /* 獲取服務器返回的信息 */
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        System.out.println(new String(buf, 0, len));

        s.close();
    }
}

二、URL類

    1.概述
      URL(Uniform Resource Locator)類代表一個同一資源定位符。該類是指向互聯網資源的指針。

    2.常用方法
      -String getFile():獲取此URL的文件名
      -String getHost():獲取此URL的主機名
      -String getPath():獲取此URL的路徑部分
      -int getPort():獲取此URL的端口號
      -String getProtocol():獲取此URL的協議名稱
      -String getQuery():獲取此URL的查詢部

    3.常用方法演示

示例代碼:

package com.heisejiuhuche.socket;

import java.net.MalformedURLException;
import java.net.URL;


public class URLDemo {
    public static void main(String[] args) throws MalformedURLException {
        URL url = new URL("http://192.168.0.100:8080/myweb/index.html/?name=hsjhc&age=25");
        /* 演示常用方法 */
        System.out.println("getProtocol: " + url.getProtocol());
        System.out.println("getHost: " + url.getHost());
        System.out.println("getPort: " + url.getPort());
        System.out.println("getPath: " + url.getPath());
        System.out.println("getFile: " + url.getFile());
        System.out.println("getQuery: " + url.getQuery());
    }
}

程序運行結果:

getProtocol: http
getHost: 192.168.0.100
getPort: 8080
getPath: /myweb/index.html/
getFile: /myweb/index.html/?name=hsjhc&age=25
getQuery: name=hsjhc&age=25

注意:
沒有指定端口,返回-1;開發時要進行判斷,如果port的值爲-1,要給port賦值爲80

三、URLConnection類

    1.概述
      URLConnection類對象是URL對象調用openConnect()方法之後的返回值,就能得到這個URL的連接對象,也就是目標主機對象;獲取URLConnection對象可以方便獲取Socket輸入流對象

    2.URLConnection類演示

示例代碼:

package com.heisejiuhuche.socket;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class URLConnectionDemo {
    public static void main(String[] args) throws Exception {
        /* 封裝URL對象 */
        URL url = new URL("http://192.168.0.100:8080/myweb/indetml/?name=hsjhc&age=25");
        /* 調用url對象的openConnection方法,得到URLConnection對象 */
        URLConnection conn = url.openConnection();
        System.out.println(conn);

        /* 因爲URLConnection對象中封裝了Socket對象
         * 所以可以調用getInputStream()方法獲取輸入流來讀取服務器端數據 */
        InputStream in = conn.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        System.out.println(new String(buf, 0, len));
    }
}

注意:
url.openConnection().getInputStream()這行代碼,可以簡寫成url.openStream();該方法是URLConnection類中獲取InputStream的便捷方法

四、擴展知識

    1.Socket空參數構造函數
      通過調用connect()方法連接主機;connect()方法可以接收一個SocketAddress對象作爲參數;SocketAddress類的子類InetSocketAddress類封裝IP地址端口;那麼以前在創建Socket對象的時候分開寫主機IP和端口,現在也可以調用connect方法,傳遞一個InetSocketAddress對象即可。

    2.ServerSocket類接收的backlog參數
      backlog表示隊列的最大長度。它代表能同時連接到服務器的最大客戶端個數。在服務器端開發時,要指定這個參數,避免服務器因連接數過多而死機。

五、域名解析

    1.概述
      IP地址不容易記憶,通常人們在訪問網站的時候,在地址欄輸入的都是類似於www.baidu.com這樣的域名。但是要訪問到網絡上對應這個域名的那個主機,需要將這個域名解析成IP地址。這個過程,需要DNS域名解析服務器來完成。DNS服務器中記錄的是每個域名對應的IP地址映射表。

    2.基本原理

這裏寫圖片描述

PC在訪問網站的時候,要走這4步流程。在瀏覽器輸入域名之後,第1步,到本機C盤``(C:\windows\Systems\drivers\etc\)下的hosts文件,尋找這個域名是否在本地有已經配置的IP地址,如果有,直接訪問該IP,否則,進入第2步;第2步,訪問公網DNS服務器,找到DNS服務器上的域名——IP映射關係;第3步,DNS服務器將對應的IP地址返回給PC;第4步,PC端訪問該IP地址,找到公網上的指定主機。

注意:
127.0.0.1與localhost之間的映射關係在本機上C盤下的hosts文件中。該文件可以用於屏蔽惡意網站。

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