網絡編程


1.網絡編程入門

計算機網絡
  • 是指將地理位置不同的具有獨立功能的多台計算機及其外部設備,通過通信線路連接起來,在網絡操作系統,網絡管理軟件及網絡通信協議的管理和協調下,實現資源共享和信息傳遞的計算機系統

image

網絡編程
  • 在網絡通信協議下,實現網絡互連的不同計算機運行的程序間可以進行數據交換
網絡編程三要素

image

(1)IP地址

IP地址:是網絡中設備的唯一標識

IP地址分為兩大類:

  • IPv4:是給每個連接在網絡上的主機分配一個32bit地址。按照TCP/IP規定,IP地址用二進制來表示,每個IP地址長32bit,也就是4個字節。例如一個采用二進制形式的IP地址是“11000000 10101000 00000001 01000010”,這么長的地址,處理起來太費勁了。為了方便數組,IP地址經常被寫成十進制形式,中間使用符號 “.” 分隔不同的字節。於是,上面的IP地址可以表示為“192.168.1.66”。IP地址的這種表示法叫做“點分十進制表示法”,這顯然比1和0容易記憶的多
  • IPv6:由於互聯網的蓬勃發展,IP地址的需求量愈來愈大,但是網絡地址資源有限,使得IP的分配越發緊張。為了擴大地址空間,通過IPv6重新定義地址空間,采用128位地址長度,每16個字節一組,分成8組十六進制數,這樣就解決了網絡地址資源數量不夠的問題
常用命名:
  • ipconfig:查看本機IP地址
  • ping IP地址:檢查網絡是否連通
  • 特殊IP地址
    • 127.0.0.1:是回送地址,可以代表本機地址,一般用來測試使用
InetAddress 的使用

為了方便我們對IP地址的獲取和操作,Java提供了一個類InetAddress 供我們使用

InetAddress :此類表示Internet協議(IP)地址

image

public static void main(String[] args) throws UnknownHostException {
    InetAddress address = InetAddress.getByName("192.168.1.66");
    String hostName = address.getHostName();
    String ip = address.getHostAddress();
    
    System.out.println("主機名:"+hostName);
    System.out.println("IP地址:"+ip);
}

(2)端口

端口:設備上應用程序的唯一標識

端口號:用兩個字節表示的整數,它的取值范圍是065535。其中,01023 之間的端口號用於一些知名的網絡服務和應用,普通的應用程序需要使用1024以上的端口號。如果端口號被另外一個服務或應用所占用,會導致當前程序啟動失敗

(3)協議

協議:計算機網絡中,連接和通信的規則被稱為網絡通信協議

UDP協議

  • 用戶數據報協議(User Datagram Protocol)

  • UDP是無連接通信協議,即在數據傳輸時,數據的發送端和接收端不建立邏輯連接。簡單來說,當一台計算機向另一台計算機發送數據時,發送端不會確認接收端是否存在,就會發出數據,同樣接收端在收到數據時,也不會向發送端反饋是否收到數據。

    由於使用UDP協議消耗資源小,通信效率高,所以通常都會用於音頻、視頻和普通數據的傳輸

  • 例如視頻會議通常采用UDP協議,因為這種情況即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。但是在使用UDP協議傳送數據時,由於UDP的面向無連接性,不能保證數據的完整性,因此在傳輸重要數據時不建議使用UDP協議

TCP協議

  • 傳輸控制協議(Transmission Control Protocol)
  • TCP協議是面向連接的通信協議,即傳輸數據之前,在發送端和接收端建立邏輯連接,然后再傳輸數據,它提供了兩台計算機之間可靠無差錯的數據傳輸。在TCP連接中必須要明確客戶端與服務器端,有客戶端想服務器端發出連接請求,每次連接的創建都需要經過“三次握手”
  • 三次握手:TCP協議中,在發送數據的准備階段,客戶端與服務器端之間的三次交互,以保證連接的可靠
    • 第一次握手,客戶端向服務器端發出連接請求,等待服務器端確認
    • 第二次握手,服務器端向客戶端回送一個響應,通知客戶端收到了連接請求
    • 第三次握手,客戶端再次向服務器端發送確認信息,確認連接

image

  • 完成三次握手后,連接建立,客戶端和服務器端就可以開始進行數據傳輸了。由於這種面向連接的特性,TCP協議可以保證傳輸數據的安全,所以應用十分廣泛。例如上傳文件、下載文件和瀏覽網頁等等

2.UDP通信程序

(1)UDP通信原理

UDP協議是一種不可靠的網絡協議,它在通信的兩端各建立一個Socket對象,但是這兩個Socket只是發送、接收數據的對象,因此對於基於UDP協議的通信雙方而言,沒有所謂的客戶端和服務器端概念。Java提供了DatagramSocket類作為基於UDP協議的Socket

(2)UDP發送數據

發送數據的步驟:
  1. 創建發送端的Socket對象(DatagramSocket)
  2. 創建數據,並把數據打包
  3. 調用DatagramSocket對象的方法發送數據
  4. 關閉發送端
// 一:
public static void main(String[] args) throws IOException{
    // 創建發送端的Socket對象(DatagramSocket)
    DatagramSocket ds = new DatagramSocket();
    // 創建數據,並把數據打包
    byte[] bys = "hello,UDP 我來了".getBytes();
    int length = bys.length;
    InetAddress address = InetAddress.getByName("192.168.1.66");
    int port = 10086;
    DatagramPacket dp = new DatagramPacket(bys,length,address,port);
    // 調用DatagramSocket對象的方法發送數據
    ds.send(dp);
    // 關閉發送端
    ds.close();
}
// 二:發送的數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束
public static void main(String[] args) throws IOException{
    // 創建發送端的Socket對象(DatagramSocket)
    DatagramSocket ds = new DatagramSocket();
    // 錄入數據,並把數據打包
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String line;
    while((line=br.readLine()) != null){
        // 當輸入的是886時,發送數據結束
        if("886".equal(line)){
            break;
        }
        // 獲取錄入的數據,並打包
        byte[] bys = line.getBytes();
        DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.1.66"),10086);
        // 調用DatagramSocket對象的方法發送數據
        ds.send(dp);
    }
    // 關閉發送端
    ds.close();
}

(3)UDP接收數據

接收數據的步驟:
  1. 創建接收端的Socket對象(DatagramSocket)
  2. 創建一個數據包,用於接收數據
  3. 調用DatagramSocket對象的方法接收數據
  4. 解析數據包,並把數據在控制台顯示
  5. 關閉接收端
// 一:
public static void main(String[] args) throws IOException{
    // 創建接收端的Socket對象(DatagramSocket)
    DatagramSocket ds = new DatagramSocket(10086);
    // 創建一個數據包,用於接收數據
    byte[] bys = new byte[1024];
    int length = bys.length;
    DatagramPacket dp = new DatagramPacket(bys,length);
    // 調用DatagramSocket對象的方法接收數據
    ds.receive(dp);
    // 解析數據包,並把數據在控制台顯示
    byte[] datas = ds.getData();
    int len = dp.getLength(); // 實際接收到的數據長度
    String datasString = new String(datas,0,len);
    System.out.println("接收到的數據是:"+datasString);
    // 關閉接收端
    ds.close();
}
// 二:因為接收端不清楚發送端什么時候停止發送,故采用死循環接收
public static void main(String[] args) throws IOException{
    // 創建接收端的Socket對象(DatagramSocket)
    DatagramSocket ds = new DatagramSocket(10086);
    while(true){
        // 創建一個數據包,用於接收數據
        byte[] bys = new byte[1024];
        int length = bys.length;
        DatagramPacket dp = new DatagramPacket(bys,length);
        // 調用DatagramSocket對象的方法接收數據
        ds.receive(dp);
        // 解析數據包,並把數據在控制台顯示
        System.out.println("接收到的數據是:"+new String(ds.getData(),0,dp.getLength()));
    }
    // 關閉接收端
    ds.close();
}

3.TCP通信程序

(1)TCP通信原理

TCP通信協議是一種可靠的網絡協議,它在通信的兩端各建立一個Socket對象,從而在通信的兩端形成網絡虛擬鏈路,一旦建立了虛擬的網絡鏈路,兩端的程序就可以通過虛擬鏈路進行通信。

Java對基於TCP協議的網絡提供了良好的封裝,使用Socket對象來代表兩端的通信端口,並通過Socket產生IO流來進行網絡通信

Java為客戶端提供了Socket類,為服務器端提供了ServerSocket類

(2)TCP發送數據

發送數據的步驟:
  1. 創建客戶端的Socket對象(Socket)
  2. 獲取輸出流,寫數據
  3. 釋放資源
// 一:
public static void main(String[] args) throws IOException{
    // 創建客戶端的Socket對象(Socket)
    Socket s = new Socket("192.168.1.66",10000);
    // 獲取輸出流,寫數據
    OutputStream os = new s.getOutputStream();
    os.write("hello,TCP,我來了".getBytes());
    // 釋放資源
    s.close();
}
// 二:發送的數據來自於鍵盤錄入,直到輸入的數據是886,發送數據結束
public static void main(String[] args) throws IOException{
    // 創建客戶端的Socket對象(Socket)
    Socket s = new Socket("192.168.1.66",10000);
    // 錄入數據,並把數據打包
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    // 封裝輸出流對象
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    String line;
    while((line=br.readLine()) != null){
        // 當輸入的是886時,發送數據結束
        if("886".equal(line)){
            break;
        }
        bw.write(line);
        bw.newLine();
        bw.flush();
    }
    // 關閉發送端
    s.close();
}

(3)TCP接收數據

接收數據的步驟:
  1. 創建服務器端的Socket對象(ServerSocket)
  2. 獲取輸入流,讀數據,並把數據顯示在控制台
  3. 釋放資源
// 一:
public static void main(String[] args) throws IOException{
    // 創建服務器端的Socket對象(ServerSocket)
    ServerSocket ss = new ServerSocket(10000);
    Socket s = ss.accept();
    // 獲取輸入流,讀數據,並把數據顯示在控制台
    InputStream is = s.getInputStream();
    byte[] bys = new byte[1024];
    int len= is.read(bys);
    System.out.println("接收到的數據是:"+new String(bys,0,len));
    // 釋放資源
    ss.close();
}
// 二:因為服務器端不清楚客戶端什么時候停止發送,故采用死循環接收
public static void main(String[] args) throws IOException{
    // 創建服務器端的Socket對象(ServerSocket)
    ServerSocket ss = new ServerSocket(10000);
    Socket s = ss.accept();
    // 解析數據包,並把數據在控制台顯示
    BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    String line;
    while((line=br.readLine()) != null){
        System.out.println("接收到的數據是:"+line);
	}
    // 釋放資源
    ss.close();
}
練習案例:
  • 客戶端:數據來自於文本文件,接收服務器反饋
  • 服務器:接收到的數據寫入文本文件,給出反饋,代碼用線程進行封裝,為每一個客戶端開啟一個線程
// 客戶端
public static void main(String[] args) throws IOException{
    // 創建客戶端的Socket對象(Socket)
    Socket s = new Socket("192.168.1.66",10000);
    // 封裝文本文件的數據
    BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));
    // 封裝輸出流對象
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    String line;
    while((line=br.readLine()) != null){
        bw.write(line);
        bw.newLine();
        bw.flush();
    }
    // 防止讀數據是阻塞式的
    s.shutdownOutput();
    // 接收反饋
    BufferedReader brClient = new BufferedReader(InputStreamReader(s.getInputStream()));
    String data = brClient.readLine();
    System.out.println("服務器端的反饋:"+data);
    // 釋放資源
    br.close();
    s.close();
}
// 服務器端
public static void main(String[] args) throws IOException{
    // 創建服務器端的Socket對象(ServerSocket)
    ServerSocket ss = new ServerSocket(10000);
    while(true){
        Socket s = ss.accept();
        // 為每一個客戶端開啟一個線程
        new Thread(new ServerThread(s)).start();
    }
    
    // 一般來說服務器是不關閉的,再次就不釋放資源
}
// 線程類
public class ServerThread implements Runnable {
    private Socket s;
    public ServerThread(Socket s){
        this.s = s;
    }
    @Override
    public void run(){
        try{
        	// 接收數據寫到文本文件中
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            // 解決客戶端多次請求,文件名稱重復沖突的問題
            int count = 0;
            File file = new File("Copy["+count+"].java");
            while(file.exists()){
                count++;
                file = new File("Copy["+count+"].java");
            }
            BufferedWriter bw = new BufferedWriter(new FileWriter(file));
            
            String line;
            while((line=br.readLine()) != null){
                bw.write(line);
                bw.newLine();
                bw.flush();
            }
            // 給出客戶端反饋
            BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            bwServer.write("文件上傳成功");
            bwServer.newLine();
            bwServer.flush();
            
            // 釋放資源
            s.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM