Java基礎之UDP協議和TCP協議簡介及簡單案例的實現


寫在前面的廢話:馬上要找工作了,做了一年的.net ,到要找工作了發現沒幾個大公司招聘.net工程師,真是坑爹呀。哎,java就java吧,咱從頭開始學唄,啥也不說了,玩命擼吧,我真可憐啊。

摘要:

本片記載剛剛學習的網絡編程的內容,網絡編程也稱 Socket 編程 、套接字編程。

什么是Socket?

用於描述ip地址和端口,是一個通信鏈的Handle。在Internet上的主機一般運行了多個服務軟件,同時提供幾種服務,每種服務都打開一個Socket,並綁定到一個端口上,不同的端口對應於不同的服務。Socket就是為了網絡編程提供的一種機制,通信的兩端都有socket,網絡通信其實就是socket間的通信,數據在兩個socket之間通過 IO 傳輸。(摘自黑馬視頻ppt).

網絡通信的三要素:

IP地址:

網絡中設備的標識,也可以用主機名識別,但ip地址唯一,主機名不唯一;

端口號:

用於標識進程的邏輯地址,是不同進程的標識;

傳輸協議:

也即通信的規則,常見的協議由 UDP 協議 和 TCP協議;

UDP協議和TCP協議

(1)UDP

UDP 為應用程序提供了一種無需建立連接就可以發送封裝的 IP 數據報的方法(百度百科截取)。

百度上講的有點復雜,不太容易懂,比較通俗的說法就是:UDP協議會把數據打包,然后扔給目標地址,但是這個包能不能扔的到目標機器上,就不管了,udp就只管扔。

所以這種通信協議的優缺點很明顯了,優點就是:速度快,效率高;缺點就是:安全性低,容易丟包

(2)TCP

傳輸控制協議(TCP,Transmission Control Protocol)是一種面向連接的、可靠的、基於字節流的傳輸層通信協議(摘自百度百科)。

通俗說法:tcp協議只在已經確定通信雙方都能聯系上對方的時候才能進行通信:

     使用tcp協議時要先建立連接;

     建立連接的過程:三次握手,如下圖,

 

 

 

 UDP協議的簡單使用

 發送數據流程

  • 創建發送端socket對象;
  • 提供數據,並將數據封裝到數據包中;
  • 通過socket服務的發送功能,將數據包發出去;
  • 釋放資源;

接收數據流程

  • 創建接收端socket對象;
  • 接收數據;
  • 解析數據;
  • 輸出數據;
  • 釋放資源;

一個案例:

創建兩個控制台程序模擬發送端和接收端,使用udp發送端發送數據,接收端接收數據。

發送端:

SendDemo.java

package com.cherish.Socket;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
 * 使用UDP協議發送數據
 *         創建發送端Socket對象
 *         創建數據並打包
 *         發送數據
 *         釋放資源
 * 
 * DatagramSocket:此類表示用來發送和接受數據,基於UDP協議
 * 
 * DatagramSocket():
 * DatagramSocket(int port):
 * */
public class SendDemo {
    public static void main(String[] args) throws IOException {
        //創建發送端Socket對象
        DatagramSocket ds = new DatagramSocket();
        //創建數據並打包  DatagramPacket表示數據包
        //數據 byte[] 設備地址ip 進程的地址 :端口號
        String s = "hello udp,i m coming";
        byte[] bys = s.getBytes();
        int length = bys.length; 
        InetAddress address = InetAddress.getByName("acer-pc");
        int port = 8888;
        DatagramPacket dp = new DatagramPacket(bys, length, address,port);
        //發送數據
        ds.send(dp);
        //釋放資源
        ds.close();
        
    }
}

 

接收端:

ReceiveDemo.java

package com.cherish.Socket;

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

/*
 * 使用UDP協議接受對象:
 *     創建接收端Socket對象
 *     接受數據
 *  解析數據
 *  輸出數據
 *  釋放資源
 * */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        //創建接收端Socket對象,此處的端口號要跟發送端一致
        DatagramSocket ds = new DatagramSocket(8888);
        //接收數據
        byte[] bys = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bys, bys.length);
        System.out.println("接受前");
        ds.receive(dp);
        System.out.println("接收后");
        //解析數據
        //InetAddress getAddress()
        InetAddress address = dp.getAddress();
        //byte[] getData
        byte[] data = dp.getData();
        int length = dp.getLength();
        //輸出數據
        System.out.println("sender ----"+address.getHostAddress());
        System.out.println(new String(data));        
    }

}

 

 運行結果:

說明:

(1)DatagramSocket類

DatagramSocket() :創建實例,通常用於客戶端編程,他並沒有特定的監聽端口,僅僅使用一個臨時的。
DatagramSocket(int port) :創建實例,並固定監聽Port端口的報文。
DatagramSocket(int port, InetAddress laddr) :這是個非常有用的構建器,當一台機器擁有多於一個IP地址的時候,由它創建的實例僅僅接收來自LocalAddr的報文。
DatagramSocket(SocketAddress bindaddr) :bindaddr對象中指定了端口和地址。

常用方法:

receive(DatagramPacket p) :接收數據報文到p中。receive方法是阻塞的,如果沒有接收到數據報包的話就會阻塞在哪里。
send(DatagramPacket p) :發送報文p到目的地。
setSoTimeout(int timeout) :設置超時時間,單位為毫秒。
close() :關閉DatagramSocket。在應用程序退出的時候,通常會主動的釋放資源,關閉Socket,但是由於異常的退出可能造成資源無法回收。所以應該在程序完成的時候,主動使用此方法關閉Socket,或在捕獲到異常后關閉Socket。

(2)DatagramPacket類

DatagramPacket類用於處理報文,將字節數組、目標地址、目標端口等數據包裝成報文或者將報文拆卸成字節數組。

DatagramPacket(byte[] buf, int length, InetAddress addr, int port) :從buf字節數組中取出offset開始的、length長的數據創建數據對象,目標地址是addr,目標端口是port;
DatagramPacket(byte buf[], int offset, int length, SocketAddress address) :從buf字節數組中取出offset開始的、length長的數據創建數據對象,目標地址是address;

常用方法:

getData() byte[] :從實例中取得報文中的字節數組編碼。

setData(byte[] buf, int offset, int length): 設置數據報包中的數據內容

 

TCP協議的簡單使用

 tcp客戶端發送數據流程:

  • 創建發送端Socket對象(創建連接),Tcp的Socket對象與Udp的有所不同,需注意;
  •  獲取輸出流對象;
  •  發送數據;
  •  釋放資源;

 tcp服務端接收數據流程:

  •  創建接收端Socket對象;
  • 監聽(阻塞):如果建立連接失敗,程序會卡在這里,不往下執行;
  • 獲取輸入流對象;
  •  獲取數據;
  •  輸出數據;
  •  釋放資源;

 案例一:客戶端發送數據,服務端接收數據;

ClietDemo.java:

package com.cherish.Socket;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

/*
 * 使用tcp協議發送數據
 *         創建發送端Socket對象(創建連接)
 *         獲取輸出流對象
 *         發送數據
 *         釋放資源
 * */
public class TcpClientDemo {
    public static void main(String[] args) throws IOException{
        //創建發送端Socket對象(創建連接)
        Socket s = new Socket(InetAddress.getByName("acer-pc"),8886); 
        //獲取輸出流對象
        OutputStream os = s.getOutputStream();
        //發送數據
        String str = "hello tcp , i m coming!";
        os.write(str.getBytes());
        //釋放資源
        s.close();
    }
}

 

ServerDemo.java: 

package com.cherish.Socket;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/*
 * 使用tcp協議接受對象
 *         創建接收端Socket對象
 *         監聽(阻塞):如果建立連接失敗,程序會卡在這里,不往下執行
 *         獲取輸入流對象
 *         獲取數據
 *         輸出數據
 *         釋放資源
 * */
public class TcpServerDemo {
    
    public static void main(String[] args) throws IOException {
        //創建接收端Socket對象
        ServerSocket ss = new ServerSocket(8886); 
        //監聽
        Socket s = ss.accept();
        //獲取輸入流對象
        InputStream is = s.getInputStream();
        //獲取數據
        byte[] bys = new byte[1024];
        int len; //用於存儲讀到的字節數
        len = is.read(bys);
        //輸出數據
        String client = s.getInetAddress().getHostName();
        System.out.println(client+"發來的數據");
        System.out.println(new String(bys,0,len));
        //釋放資源
        s.close();
        //ss.close(); //socket對象一般不釋放,因為客戶端不止一個,可能有多個客戶端會發送數據        
    }    
}

 運行結果:

 

 案例二:對案例一進行改進,服務端收到數據后,將數據由小寫換成大寫,然后返回給客戶端:

Client.java:

package com.cherish.Socket.TcpDemo_2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
    public static void main(String[] args) throws IOException {
        //創建發送端socket對象
        Socket s = new Socket(InetAddress.getByName("acer-pc"),8866);
        //獲取輸出流對象
        OutputStream os = s.getOutputStream();
        //寫入數據
        String str = "hello tcp ,ahahahah!!!";
        os.write(str.getBytes());
        System.out.println("數據已發送");
        //獲取輸入流對象
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        String backStr = new String(bys,0,len);
        System.out.println(backStr);
        s.close();        
    }
}

 Server.java:

package com.cherish.Socket.TcpDemo_2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        //創建服務端接收對象
        ServerSocket ss = new ServerSocket(8866);
        //監聽 來自客戶端的連接
        Socket s = ss.accept();
        //獲取輸入流對象
        InputStream is = s.getInputStream();
        //獲取數據
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        String str = new String(bys,0,len);
        //輸出數據
        System.out.println(str);
        //轉換數據
        String upperStr = str.toUpperCase();
        //獲取輸出流對象
        OutputStream os = s.getOutputStream();
        //寫入數據到流中
        os.write(upperStr.getBytes());
        //釋放資源
        s.close();
    }
}

運行結果:

案例三:用tcp模擬一個登錄功能,tcp客戶端輸入用戶名和密碼,然后發送給服務端,服務端根據輸入結果返回登錄或失敗給客戶端;

LoginClient.java:

package com.cherish.Socket.LoginCase;

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

/*
 * 模擬用戶登錄案例
 * */
public class LoginClient {
    public static void main(String[] args) throws IOException {
        //創建客戶端socket對象
        Socket s = new Socket("acer-pc",8885);
        //獲取用戶名和密碼
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("請輸入用戶名");
        String userName = br.readLine();
        System.out.println("請輸入密碼");
        String password = br.readLine();        
        //獲取輸出流對象
        PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        //寫出數據
        out.println(userName);
        out.println(password);
        //獲取輸入流對象
        BufferedReader serverBr = new BufferedReader(new InputStreamReader(s.getInputStream()));
        //獲取服務端返回的數據
        String backStr = serverBr.readLine();
        System.out.println(backStr);
        //關閉資源    
        s.close();
    }
}

 

 LoginServer.java:

package com.cherish.Socket.LoginCase;

import java.io.BufferedReader;
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 IOException {
        //創建服務端socket對象
        ServerSocket ss = new ServerSocket(8885);
        //監聽
        Socket s = ss.accept();
        //獲取輸入流對象
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        //獲取用戶名和密碼
        String userName = br.readLine();
        String password = br.readLine();
        //判斷用戶名和密碼是否正確
        boolean flag = false;
        if (userName.equals("cherish")&&password.equals("123")) {
            flag = true;
        }
        //獲取輸出流對象
        PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        //返回判斷信息
        if (flag) {
            System.out.println("成功");
            out.println("登錄成功");
        }else {
            out.println("登陸失敗");
        }
        //釋放資源
        s.close();
        //ss.close();//服務端一般不關閉                
    }
}

 

運行結果:

 

 

 結語:本篇博客根據傳智播客基礎視頻整理,記載比較簡單,但大致能演示清楚udp和tcp的區別及其用法。個人學習java才不到4天時間,對於java的一些知識點不能講解的很清楚,因此代碼注釋寫的不是很詳細,見諒見諒!哈哈!

本片博客的代碼我上傳到百度雲盤,需要的可自取:

鏈接:https://pan.baidu.com/s/1-dTfG9GY5MBIAlNJg1v0mw 
提取碼:3dw6 
復制這段內容后打開百度網盤手機App,操作更方便哦

 


免責聲明!

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



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