1. 程式人生 > 其它 >Java學習筆記 第十章 網路程式設計

Java學習筆記 第十章 網路程式設計

JAVA學習筆記第十章

10.網路程式設計

10.1引入

TCP建立連線:

釋放連線:

10.2InetAddress & InetSocketAddress

InetAddress有兩個方法:

InetSocketAddress封裝了ip和埠號

10.3套接字

10.4TCP通訊

10.4.1客戶端和服務端通訊

客戶端傳送一句話到伺服器

public class TestClient { // 客戶端
    public static void main(String[] args) throws IOException {
        // 建立套接字:指定伺服器ip和埠號
        Socket s = new Socket("127.0.0.1", 8888);
        //對程式設計師來說,向外傳送資料 感受 --》 利用輸出流;
        OutputStream os = s.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);
        //利用OutputStream就可以向外傳送資料流,但是沒有直接傳送String的方法
        //所以我們又在OutputStream外面套了一個處理流:DataOutputStream
        dos.writeUTF("你好");

        // 關閉流 + 關閉網路資源
        dos.close();
        os.close();
        s.close();

    }
}
public class TestServer { // 伺服器
    public static void main(String[] args) throws IOException {
        // 1.建立套接字:指定伺服器的埠
        ServerSocket ss = new ServerSocket(8888);
        // 2.等待客戶端傳送來的資訊
        Socket s = ss.accept(); // 阻塞方法:等待客戶端的資料,什麼時候收到資料,什麼時候程式繼續向下執行
        // 3.accept返回值為Socket,這個Socket其實就是客戶端的Socket
        //接到這個Socket之後,客戶端和服務端才真正產生了連線,才真正可以通訊了
        //感受到的操作流
        InputStream is = s.getInputStream();
        DataInputStream dis = new DataInputStream(is);

        // 4.讀取客戶端發來的資料
        String str = dis.readUTF();
        System.out.println("客戶端發來的資料為:" + str);

        // 5.關閉流 + 關閉網路資源
        dis.close();
        is.close();
        s.close();
    }
}

先開伺服器,然後開啟客戶端,在伺服器端可以看到控制檯打印出客戶端發來的資料為:你好

如果先開客戶端,會報出連線被拒絕的錯誤

服務端也可以向客戶端傳送資訊

public class TestServer { // 伺服器
    public static void main(String[] args) throws IOException {
        // 1.建立套接字:指定伺服器的埠
        ServerSocket ss = new ServerSocket(8888);
        // 2.等待客戶端傳送來的資訊
        Socket s = ss.accept(); // 阻塞方法:等待客戶端的資料,什麼時候收到資料,什麼時候程式繼續向下執行
        // 3.accept返回值為Socket,這個Socket其實就是客戶端的Socket
        //接到這個Socket之後,客戶端和服務端才真正產生了連線,才真正可以通訊了
        //感受到的操作流
        InputStream is = s.getInputStream();
        DataInputStream dis = new DataInputStream(is);

        // 4.讀取客戶端發來的資料
        String str = dis.readUTF();
        System.out.println("客戶端發來的資料為:" + str);

        // 6.向客戶端傳送資料
        OutputStream os = s.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);
        dos.writeUTF("伺服器端接收到你的訊息");

        // 5.關閉流 + 關閉網路資源
        dos.close();
        os.close();
        dis.close();
        is.close();
        s.close();
    }
}

public class TestClient { // 客戶端
    public static void main(String[] args) throws IOException {
        // 建立套接字:指定伺服器ip和埠號
        Socket s = new Socket("127.0.0.1", 8888);
        //對程式設計師來說,向外傳送資料 感受 --》 利用輸出流;
        OutputStream os = s.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);
        //利用OutputStream就可以向外傳送資料流,但是沒有直接傳送String的方法
        //所以我們又在OutputStream外面套了一個處理流:DataOutputStream
        dos.writeUTF("你好");

        //客戶端接收伺服器的資料
        InputStream is = s.getInputStream();
        DataInputStream dis = new DataInputStream(is);
        String str = dis.readUTF();
        System.out.println("伺服器對我說:" + str);

        // 關閉流 + 關閉網路資源
        dis.close();
        is.close();
        dos.close();
        os.close();
        s.close();

    }
}

10.4.2登入驗證

模擬登入時輸入賬號密碼,並進行驗證的過程

public class User implements Serializable {
    private static final long serialVersionUID = 6158646660109509935L;
    private String name;
    private String pwd;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public User(String name, String pwd) {
        this.name = name;
        this.pwd = pwd;
    }
}

需要實現Serializable介面,並且通過idea建立UID

public class Client {  //客戶端
    public static void main(String[] args) throws IOException {
        // 1.建立套接字
        Socket s = new Socket("127.0.0.1", 8888);

        // 2.錄入賬號和密碼
        Scanner sc = new Scanner(System.in);
        System.out.println("username:");
        String name = sc.next();
        System.out.println("password:");
        String pwd = sc.next();

        // 3.將賬號和密碼封裝為一個User物件
        User user = new User(name, pwd);

        // 4.向外傳送物件,利用輸出流
        OutputStream os = s.getOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);
        oos.writeObject(user);

        // 5.接收服務端傳送的結果
        InputStream is = s.getInputStream();
        DataInputStream dis = new DataInputStream(is);
        Boolean flag = dis.readBoolean();
        System.out.println(flag);

        // 6.關閉流 + 網路資源
        dis.close();
        is.close();
        oos.close();
        os.close();
        s.close();
    }
}

public class Server {  //服務端
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 1.建立套接字
        ServerSocket ss = new ServerSocket(8888);

        // 2.等待客戶端發來的資訊
        Socket s = ss.accept();

        // 3.獲取傳入的資料
        InputStream is = s.getInputStream();
        ObjectInputStream ois = new ObjectInputStream(is);

        // 4.讀取客戶端傳送的資料
        User user = (User) ois.readObject();

        // 5.對物件進行驗證:
        boolean flag = false;
        if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
            flag = true;
        }

        // 6.向客戶端輸出結果
        OutputStream os = s.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);
        dos.writeBoolean(flag);

        // 7.關閉流 + 網路資源
        dos.close();
        os.close();
        ois.close();
        is.close();
        ss.close();
    }
}

10.4.3完整的異常處理方式

Socket提出去,其他的流在外面初始化為null

關閉流和資源放到finally,再對裡面的流進行異常處理

public class Client {  //客戶端
    public static void main(String[] args){
        // 1.建立套接字
        Socket s = null;
        OutputStream os = null;
        ObjectOutputStream oos = null;
        InputStream is = null;
        DataInputStream dis = null;
        try {
            s = new Socket("127.0.0.1", 8888);
            // 2.錄入賬號和密碼
            Scanner sc = new Scanner(System.in);
            System.out.println("username:");
            String name = sc.next();
            System.out.println("password:");
            String pwd = sc.next();

            // 3.將賬號和密碼封裝為一個User物件
            User user = new User(name, pwd);

            // 4.向外傳送物件,利用輸出流
            os = s.getOutputStream();
            oos = new ObjectOutputStream(os);
            oos.writeObject(user);

            // 5.接收服務端傳送的結果
            is = s.getInputStream();
            dis = new DataInputStream(is);
            Boolean flag = dis.readBoolean();
            System.out.println(flag);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 6.關閉流 + 網路資源
            try {
                if(dis != null) {
                    dis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(oos != null) {
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(s != null) {
                    s.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
    }
}

public class Server {
    public static void main(String[] args){
        // 1.建立套接字
        ServerSocket ss = null;
        InputStream is = null;
        ObjectInputStream ois = null;
        OutputStream os = null;
        DataOutputStream dos = null;
        
        try {
            ss = new ServerSocket(8888);
            // 2.等待客戶端發來的資訊
            Socket s = ss.accept();

            // 3.獲取傳入的資料
            is = s.getInputStream();
            ois = new ObjectInputStream(is);

            // 4.讀取客戶端傳送的資料
            User user = null;
            try {
                user = (User) ois.readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            // 5.對物件進行驗證:
            boolean flag = false;
            if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
                flag = true;
            }

            // 6.向客戶端輸出結果
            os = s.getOutputStream();
            dos = new DataOutputStream(os);
            dos.writeBoolean(flag);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 7.關閉流 + 網路資源
            try {
                if(dos != null) {
                    dos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(ss != null) {
                    ss.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        

        
    }
}

10.4.4多個客戶端和一個伺服器

客戶端的程式碼不變

public class Client {  //客戶端
    public static void main(String[] args){
        // 1.建立套接字
        Socket s = null;
        OutputStream os = null;
        ObjectOutputStream oos = null;
        InputStream is = null;
        DataInputStream dis = null;
        try {
            s = new Socket("127.0.0.1", 8888);
            // 2.錄入賬號和密碼
            Scanner sc = new Scanner(System.in);
            System.out.println("username:");
            String name = sc.next();
            System.out.println("password:");
            String pwd = sc.next();

            // 3.將賬號和密碼封裝為一個User物件
            User user = new User(name, pwd);

            // 4.向外傳送物件,利用輸出流
            os = s.getOutputStream();
            oos = new ObjectOutputStream(os);
            oos.writeObject(user);

            // 5.接收服務端傳送的結果
            is = s.getInputStream();
            dis = new DataInputStream(is);
            Boolean flag = dis.readBoolean();
            System.out.println(flag);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 6.關閉流 + 網路資源
            try {
                if(dis != null) {
                    dis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(oos != null) {
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(s != null) {
                    s.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

服務端 Server.java

public class Server {
    public static void main(String[] args){
        // 1.建立套接字
        ServerSocket ss = null;
        Socket s = null;
        int count = 0; // 定義一個計數器,用來計數 客戶端的請求
        try {
            ss = new ServerSocket(8888);
            // 2.等待客戶端發來的資訊
            while (true) { // 加入死迴圈,伺服器一直監聽客戶端是否傳送資料
                s = ss.accept();
                //每次過來的客戶端的請求靠執行緒處理
                new ServerThread(s).start();
                count++;
                System.out.println("當前是第"+count+"個使用者訪問我們的伺服器,對應的使用者是"+s.getInetAddress());
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

新建ServerThread.java

public class ServerThread extends Thread{  // 執行緒:專門處理客戶端的請求
    InputStream is = null;
    ObjectInputStream ois = null;
    OutputStream os = null;
    DataOutputStream dos = null;
    Socket s = null;
    public ServerThread(Socket s){
        this.s = s;
    }
    @Override
    public void run() {
        // 3.獲取傳入的資料
        try{
            is = s.getInputStream();
            ois = new ObjectInputStream(is);

            // 4.讀取客戶端傳送的資料
            User user = null;
            try {
                user = (User) ois.readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            // 5.對物件進行驗證:
            boolean flag = false;
            if (user.getName().equals("wrr") && user.getPwd().equals("1234")){
                flag = true;
            }

            // 6.向客戶端輸出結果
            os = s.getOutputStream();
            dos = new DataOutputStream(os);
            dos.writeBoolean(flag);
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            try {
                if(dos != null) {
                    dos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

10.5UDP通訊

10.5.1單向通訊

public class TestSend {
    public static void main(String[] args) throws IOException { //傳送方
        System.out.println("學生上線");
        // 1.準備套接字,指定傳送方的埠號
        DatagramSocket ds = new DatagramSocket(8888);
        // 2.準備資料包
        String str = "你好";
        byte[] bytes = str.getBytes();
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 9999);
        /*
        需要四個引數: 1.傳送資料轉為位元組的陣列 2.位元組陣列的長度 3.接收方的IP地址 4.接收方的埠號
         */

        // 3.傳送
        ds.send(dp);

        // 4.關閉資源
        ds.close();

    }
}

public class TestReceive {
    public static void main(String[] args) throws IOException { //接收方
        System.out.println("接收訊息");
        // 1.準備套接字
        DatagramSocket ds = new DatagramSocket(9999);

        // 2.有一個空的資料包,準備接收對方傳過來的資料
        byte[] b = new byte[1024];
        DatagramPacket dp = new DatagramPacket(b, b.length);

        // 3.接收對方的資料包,然後放入到我們的dp資料包中填充
        ds.receive(dp); //接收完以後,dp裡面就填充好內容了

        // 4.取出資料:
        byte[] data = dp.getData();
        String s = new String(data, 0, dp.getLength());  // dp.getLength()陣列包中的有效長度
        System.out.println(s);

        // 5.關閉資源
        ds.close();

    }
}

10.5.2雙向通訊

public class TestReceive {
    public static void main(String[] args) throws IOException { //接收方
        System.out.println("接收訊息");
        // 1.準備套接字
        DatagramSocket ds = new DatagramSocket(9999);

        // 2.有一個空的資料包,準備接收對方傳過來的資料
        byte[] b = new byte[1024];
        DatagramPacket dp = new DatagramPacket(b, b.length);

        // 3.接收對方的資料包,然後放入到我們的dp資料包中填充
        ds.receive(dp); //接收完以後,dp裡面就填充好內容了

        // 4.取出資料:
        byte[] data = dp.getData();
        String s = new String(data, 0, dp.getLength());  // dp.getLength()陣列包中的有效長度
        System.out.println(s);

        // 5.回覆
        String str = "Hello";
        byte[] bytes = str.getBytes();
        DatagramPacket dp1 = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 8888);

        ds.send(dp1);

        // 5.關閉資源
        ds.close();

    }
}

public class TestSend {
    public static void main(String[] args) throws IOException { //傳送方
        System.out.println("學生上線");
        // 1.準備套接字,指定傳送方的埠號
        DatagramSocket ds = new DatagramSocket(8888);
        // 2.準備資料包
        String str = "你好";
        byte[] bytes = str.getBytes();
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 9999);
        /*
        需要四個引數: 1.傳送資料轉為位元組的陣列 2.位元組陣列的長度 3.接收方的IP地址 4.接收方的埠號
         */

        // 3.傳送
        ds.send(dp);

        // 4.接收
        byte[] b = new byte[1024];
        DatagramPacket dp1 = new DatagramPacket(b, b.length);
        ds.receive(dp1);

        byte[] data = dp1.getData();
        String s = new String(data, 0, dp1.getLength());
        System.out.println(s);

        // 4.關閉資源
        ds.close();

    }
}

10.5.3完整的異常處理方式

與TCP協議的通訊一致,如10.4.3