天天看點

Java 網絡程式設計(大全)前言必讀

前言必讀

讀者手冊(必讀)_雲邊的快樂貓的部落格-CSDN部落格

一、1網絡通信的基本模式分為兩種

1.CS模式(Client---用戶端,Server---服務端) 

用戶端是需要程式員去開發的,例如日常使用的各種的APP,服務端就是伺服器。

例子:端遊,依賴特定的PC端才能玩。

2.BS模式(Browser---浏覽器,Server---服務端)---重點學習

服務端是需要程式員去開發的。浏覽器就是我們日常使用的浏覽器去通路各種網站,隻要有浏覽器就可以通路,不依賴特定的PC端,在任意裝置上都可以通路網站伺服器 。

例子:網頁小遊戲,聯網浏覽器就可以玩。 

一、2 請分析C/S和B/S架構相比的各自的優缺點?

1.CS(用戶端--伺服器結構)

優點 :技術成熟,互動性強,網絡通信量低,響應資料快。将任務分到了兩端,降低了系統的開銷。用戶端要處理大多數的業務邏輯和UI展示。他是胖用戶端。

缺點:更新太快,要求使用者有相同的作業系統,如果有不同的作業系統還要開發不同的版本,對于計算機電腦的配置要求也高

2。BS(浏覽器-伺服器結構)

優點:主要事務在服務端實作。分布性強、維護友善、開發簡單

共享性強、總體成本低,對用戶端的電腦組態要求較低

缺點:資料安全性問題,對伺服器要求高。資料傳輸速度較慢,軟體的個性化明顯降低,難以實作傳統模式下的特殊功能要求,他是瘦用戶端,大量的資料的傳輸都要通過伺服器與浏覽器進行互動,通信開銷大,難以實作複雜的應用構造

二、實作網絡程式設計的三種要素

IP位址可以了解為具體哪個計算機,端口了解計算機上的程式(一個程式一個端口),協定了解為電腦通過什麼方式和外界互動

要素一:IP位址:裝置在網絡中的位址,是唯一的辨別

詳解:

(1)常見的IP分類為:

IPv4(32比特4位元組)和IPv6(128位16個位元組)--稱号--可以标記地球上的每一粒沙子。

(2)IP位址的取經之路:

計算機:我要去找百度擷取資料。

DNS伺服器:發過來我看看哪個網址域名啊,給你指路具體的ip位址

計算機:知道了這個ip位址,我就可以去找具體要通路的伺服器了

伺服器:計算機老弟你來找我了啊,那我把你要的資料發給你吧。 

(3)公網位址和私有位址(區域網路使用)

192.168開頭的就是常見的私有位址 

(4)擷取IP位址的代碼

 要去實作這個IP位址的擷取就要用到 InetAddress方法

代碼例子:

package bao;

import java.net.InetAddress;

public class Test {
    public static void main(String[] args) throws Exception {
        //1.擷取本機位址ip對象
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());//擷取主機名字
        System.out.println(ip1.getHostAddress());//擷取ip位址
        //2.擷取域名ip對象
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostName());//擷取域名
        System.out.println(ip2.getHostAddress());//擷取域名的ip位址
        //3.擷取公網對象
        InetAddress ip3 = InetAddress.getByName("112.80.248.76");
        System.out.println(ip3.getHostName());//擷取公網名字
        System.out.println(ip3.getHostAddress());//擷取公網ip位址
        //判斷網絡是否能連接配接通信 ping 5s之前測試是否能通過
        System.out.println(ip3.isReachable(5000));//通過會傳回true
    }
}
           

運作結果:

涉及到個人隐私就不放到這裡來了,可以自己運作試試看  

 =========================================================================

要素二:端口:應用程式在裝置中的唯一辨別

一個主機裝置中,端口号是唯一的

(1)端口号:一個程式一個端口号,被規定為16位的二進制,範圍是0~65535

(2)周知端口:0~1023,被預先定義的知名應用占用。(例如:HTTP占用80端口,FTP占用21端口)

(3)注冊端口:1024~49151,配置設定給使用者程序或某些程式(例如:Tomcat占用8080端口)

(4)動态端口:49152~65535,不固定配置設定到某種程序,動态配置設定

 =========================================================================

要素三:協定:資料在網絡中的傳輸協定,最常見的有UDP和TCP(重點)

Java 網絡程式設計(大全)前言必讀

(1)TCP協定: (安全,有連接配接确認可靠)

使用TCP協定,雙方必須先建立連接配接,它是一種面向連接配接的可靠通信協定,傳輸前,要建立三次握手方式建立連接配接确認。連接配接和發送資料都需要确認。傳輸完成後,還需要釋放已連接配接的通信,通信效率相對比較低。

使用場景:對安全需求較高的檔案下載下傳、金融資料通信等。

三次握手連接配接:

用戶端:在嗎?

服務端:在

用戶端:上号

四次握手斷開連接配接:

用戶端:不玩了

服務端:好吧

服務端:下次什麼時候玩?

用戶端:有空再玩 

例子:

一收一發

發送端(用戶端) 

package bao;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

//發送端
public class Test {
    public static void main(String[] args) throws IOException {
        System.out.println("==============用戶端啟動===============");
        //1.建立發送通信管道
        Socket socket = new Socket("127.0.0.1",7777);//參數一:服務端位址 參數二:服務端端口
        //2.從scoket管道中獲得一個位元組輸出流,負責發送資料
        OutputStream os = socket.getOutputStream();
        //3.位元組流更新成列印流
        PrintStream ps = new PrintStream(os);
        //4.發送消息
        ps.println("大哥,我來了");
        ps.flush();//重新整理
    }
}
           

 接收端 (服務端)

package bao;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

//服務端
public class Test1 {
    public static void main(String[] args) throws IOException {
        System.out.println("========服務端啟動============");
        //1.建立接收管道,注冊端口
        ServerSocket serverSocket = new ServerSocket(7777);//參數一:定義服務端口
        //2.等待管道連接配接
        Socket accept = serverSocket.accept();
        //3.從管道中擷取一個位元組輸入流
        InputStream is = accept.getInputStream();
        //4.位元組流更新生緩沖輸入流
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        //5.按照行讀取消息會更好
        String a;
        if ((a = br.readLine())!=null){
            System.out.println(accept.getRemoteSocketAddress()+"說了:"+a);
        }
    }
}
           

 運作結果:

/127.0.0.1:60316說了:大哥,我來了
 ==============用戶端啟動===============

多收多發 

發送端(用戶端) 

package bao;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;


public class kehu {
    public static void main(String[] args) throws Exception {
        System.out.println("用戶端啟動");
        //1.建立與服務端連接配接的管道
        Socket s = new Socket(InetAddress.getLocalHost(), 9966);
        //2.建立一個線程負責用戶端的消息讀取
        new ClientReaderThread(s).start();
        //3.建立一個位元組輸入流管道
        OutputStream o = s.getOutputStream();
        PrintStream p = new PrintStream(o);//更新流
        //4.用戶端輸入資料
        Scanner sc = new Scanner(System.in);
        while (true){
            System.out.println("請輸入:");
            String s1 = sc.nextLine();
            p.println(s1);//發送資料出去
            p.flush();//重新整理流
        }
    }
}
class ClientReaderThread extends Thread{
    private Socket socket;
    public ClientReaderThread(Socket socket){
        this.socket = socket;
    }
        @Override
        public void run(){
            try {
                //把位元組輸入流包裝成字元輸入流
                InputStream i = socket.getInputStream();
                BufferedReader b = new BufferedReader(new InputStreamReader(i));
                String s1;
                while (true){
                    if ((s1=b.readLine())!=null){
                        System.out.println("收到了消息"+s1);
                    }
                }
            } catch (IOException e) {
                System.out.println("伺服器把你提出群聊");
            }
        }
    }

           

  接收端 (服務端)

package bao;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class fuwu {
    //1.定義一個靜态變量儲存全部管道
    public static List<Socket> all_Sockets = new ArrayList<>();
    public static void main(String[] args) throws IOException {
        System.out.println("服務端啟動成功");
        //2.服務端口注冊
        ServerSocket ss = new ServerSocket(9966);
        //3.管道死循環設定
        while (true){
            Socket s = ss.accept();
            System.out.println(s.getRemoteSocketAddress()+"上線了");
            all_Sockets.add(s);
            new fuwuThread(s).start();
        }
    }
}
class fuwuThread extends Thread{
    private Socket socket;
    public fuwuThread(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run(){
        try {
            InputStream i = socket.getInputStream();
            BufferedReader b = new BufferedReader(new InputStreamReader(i));
            String s1;
            while ((s1=b.readLine())!=null){
                System.out.println(socket.getRemoteSocketAddress()+"說"+s1);
                sendMessage(s1);
            }
        } catch (IOException e) {
            System.out.println(socket.getRemoteSocketAddress()+"離線了");
            fuwu.all_Sockets.remove(socket);
        }
    }
    private void sendMessage (String s1) throws IOException {
        for (Socket s:fuwu.all_Sockets){
            OutputStream o = s.getOutputStream();
            PrintStream p = new PrintStream(o);
            p.println(s1);
            p.flush();
        }
    }
}
           

運作結果:

服務端啟動成功

/192.168.78.1:56384上線了

/192.168.78.1:56384說大哥

/192.168.78.1:56389上線了

/192.168.78.1:56389說小弟

/192.168.78.1:56384說

/192.168.78.1:56384說大哥11

用戶端啟動

請輸入:

大哥

請輸入:

收到了消息大哥

收到了消息小弟

大哥11

請輸入:

請輸入:

收到了消息

收到了消息大哥11

用戶端啟動

請輸入:

小弟

請輸入:

收到了消息小弟

收到了消息

收到了消息大哥11 

 =========================================================================

                                                            TCP和UDP的分界線

 =========================================================================

(2)UDP協定:(速度快,無連接配接,不可靠)

不需要建立連接配接(因為把資料源IP、目的地IP、端口封裝成資料包),每個資料包在64KB内,隻管發,不管對方有沒有接到确認什麼的。

優點:可以廣播發送,發送資料結束時無需釋放資源,開銷小,速度快。

使用場景:語言通話、視訊通話等。

ps:這個就是一股腦的什麼都封裝一起,直接往外抛就什麼都不管了,當然快了。

例子:

一收一發

發送端(用戶端)

package bao;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Test {
    public static void main(String[] args) throws Exception {
        //一、發送端(測試時候先啟動接收再發送端)
        //1.建立發送端對象,發送端自帶預設端口号(人)
        System.out.println("========用戶端啟動============");
        DatagramSocket socket1 = new DatagramSocket();//不定義就預設端口

        //2.建立一個要發送的資料容器(容器裡面有資料)
        byte[] a ="我是水".getBytes();
        //3.建立一個資料包對象把容器裝起來
        DatagramPacket packet1 = new DatagramPacket(a,a.length, InetAddress.getLocalHost(),8899);//資料,大小,服務端的IP,服務端的端口
        //4.發送出去
        socket1.send(packet1);
        //5.關閉資源,避免資源浪費
       socket1.close();
    }
}
           

接收端 (服務端)

package bao;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Test1 {
    public static void main(String[] args) throws Exception {
        //二、接收端(測試時候先啟動接收再發送端)
        //1.建立接收端對象,注冊端口(人)
        System.out.println("=========接收端啟動===============");
        DatagramSocket socket2 = new DatagramSocket(8899);

        //2.建立一個要接收的資料容器(等待接收資料)
        byte[]b =new byte[1024*64];
        //3.把容器資料打包
        DatagramPacket packet2 = new DatagramPacket(b,b.length);
        //4.等待接收資料
        socket2.receive(packet2);
        //5.讀取多少倒出多少
        int len = packet2.getLength();
        String rs = new String(b,0,len);
        System.out.println("接收到了資料了"+rs);

        //6.關閉資源,避免資源浪費
        socket2.close();
    }
}
//額外知識點,擷取對方端口和ip位址
//String ip = packet2.getAddress().toString();
//System.out.println("對方IP位址為"+ip);

//int port = packet2.getPort();
//System.out.println("對方端口位置"+port);
           

運作結果:

========用戶端啟動============ 

=========接收端啟動===============

接收到了資料了我是水 

多收多發

思想:把一收一發代碼拿來改進就好了

發送端:把主要發送的代碼寫入死循環并寫一個鍵盤輸入代碼,隻有使用者輸入exit才能退出循環。

接收端:把等待接收的封裝包開始寫入死循環裡面,然後再把釋放資源的代碼注釋掉才能一直接收

發送端(用戶端)

package bao;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
//發送端
public class Test {
    public static void main(String[] args) throws Exception {
        //一、1.建立發送端對象,發送端自帶預設端口号(人)
        DatagramSocket socket1 = new DatagramSocket();//不定義就預設端口
        System.out.println("========用戶端啟動============");
        //二.6建立鍵盤錄入
        Scanner sc = new Scanner(System.in);
        while (true) {//二、5.死循環把代碼封起來(多收多發步驟)
            //二、6.接收鍵盤錄入
            System.out.println("請輸入:");
            String msg = sc.nextLine();

            //二、7.設定exit退出
            if ("exit".equals(msg)){
                System.out.println("離線成功");
                socket1.close();//釋放資源
                break;
            }

            //2.建立一個要發送的資料容器(容器裡面有資料)
            byte[] a =msg.getBytes();
            //3.建立一個資料包對象把容器裝起來
            DatagramPacket packet1 = new DatagramPacket(a,a.length, InetAddress.getLocalHost(),8899);//資料,大小,服務端的IP,服務端的端口
            //4.發送出去
            socket1.send(packet1);
        }
    }
}
           

接收端 (服務端)

package bao;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
//二、接收端
public class Test1 {
    public static void main(String[] args) throws Exception {
        //1.建立接收端對象,注冊端口(人)
        System.out.println("=========接收端啟動===============");
        DatagramSocket socket2 = new DatagramSocket(8899);

        //2.建立一個要接收的資料容器(等待接收資料)
        byte[]b =new byte[1024*64];
        //3.把容器資料打包
        DatagramPacket packet2 = new DatagramPacket(b,b.length);
        while (true) {//二、6.把封裝代碼寫入死循環并删掉釋放資源的代碼(多收多發步驟)
            //4.等待接收資料
            socket2.receive(packet2);
            //5.讀取多少倒出多少
            int len = packet2.getLength();
            String rs = new String(b,0,len);
            System.out.println("接收到了來自:"+packet2.getAddress()+"對方端口是:"+packet2.getPort()+rs);
        }
    }
}
//額外知識點,擷取對方端口和ip位址
//String ip = packet2.getAddress().toString();
//System.out.println("對方IP位址為"+ip);

//int port = packet2.getPort();
//System.out.println("對方端口位置"+port);
           

運作結果: 

========用戶端啟動============

請輸入:

你在嗎

請輸入:

在幹嘛

請輸入: 

=========接收端啟動===============

接收到了來自:(隐私不展示)對方端口是:(隐私不展示)你在嗎

接收到了來自:(隐私不展示)對方端口是:(隐私不展示)在幹嘛

多開用戶端 步驟

一台主機中可以建立很多個用戶端給接收端發消息 

 1.在用戶端的頁面右上角點選

Java 網絡程式設計(大全)前言必讀

2.找到Allow multiple instances勾選完成

Java 網絡程式設計(大全)前言必讀

 廣播(目前主機和所在網絡中的所有主機通信)

步驟:(前提在同一網段内)

發送端的參數位址指定為255.255.255.255,并且指定端口:例如6666

接收端的參數端口隻要比對成功就可以了,例如:6666

例子:

發送端

//修改前

 //3.建立一個資料包對象把容器裝起來
DatagramPacket packet1 = new DatagramPacket(a,a.length, InetAddress.getLocalHost(),8899);//資料,大小,服務端的IP,服務端的端口

//修改後

 //3.建立一個資料包對象把容器裝起來
DatagramPacket packet1 = new DatagramPacket(a,a.length, InetAddress.getByName("255.255.255.255"),6666);//資料,大小,服務端的IP,服務端的端口
           

 接收端

//修改前
        //1.建立接收端對象,注冊端口(人)
System.out.println("=========接收端啟動===============");
DatagramSocket socket2 = new DatagramSocket(8899);

//修改後
        //1.建立接收端對象,注冊端口(人)
System.out.println("=========接收端啟動===============");
DatagramSocket socket2 = new DatagramSocket(6666);
           

多點傳播 (目前主機和所在網絡中的一組主機通信)

發送端和接收端都要用MulticastSocket綁定同一個IP位址和綁定同一個端口

範圍:224.0.0.0~~~~239.255.255.255

發送端

//修改前
            //3.建立一個資料包對象把容器裝起來
DatagramPacket packet1 = new DatagramPacket(a,a.length, InetAddress.getByName("255.255.255.255"),6666);//資料,大小,服務端的IP,服務端的端口

//修改後
            //3.建立一個資料包對象把容器裝起來
DatagramPacket packet1 = new DatagramPacket(a,a.length, InetAddress.getByName("224.0.1.1"),6666);//資料,大小,服務端的IP,服務端的端口
           

接收端

//修改前

         //1.建立接收端對象,注冊端口(人)
System.out.println("=========接收端啟動===============");
DatagramSocket socket2 = new DatagramSocket(6666);

//修改後
        //1.建立接收端對象,注冊端口(人)
System.out.println("=========接收端啟動===============");
MulticastSocket socket2 = new MulticastSocket(6666);
//綁定多點傳播IP(JDK開始過時的API)
socket2.joinGroup(InetAddress.getByName("224.0.1.1"));