通信瞭解五元組
- 源IP
- 源端口
- 目的IP
- 目的端口
- 協議類型
端口號(port)是傳輸層協議的內容.
端口號是一個32位的整數;
端口號用來標識一個進程, 告訴操作系統, 當前的這個數據要交給哪一個進程來處理;
IP地址 + 端口號能夠標識網絡上的某一臺主機的某一個進程;
一個端口號只能被一個進程佔用
源IP就好比發件人的地址 源端口就好比發件人的姓名
目的IP就好比收件人的地址 目的端口就好比收件人的姓名
socket編程接口
- DatagramSocket(int port,InetAddress laddr) 創建一個數據報套接字,綁定到指定的本地地址
- DatagramSocket(SocketAddress bindaddr) 創建一個數據報套接字,綁定到指定的本地套接字地址
- void bind(SocketAddress addr) 將此DatagramSocket綁定到特定的地址和端口
- void connect(InetAddress address, int port) 將套接字連接到此套接字的遠程地址
- void receive(DatagramPacket p) 從此套接字接收數據報包
- void close() 關閉此數據報套接字
- void send(DatagramPacket p) 從此套接字發送數據報包
使用簡單的UDP網絡程序實現服務器\
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
//服務器程序
public class MyUDPServer {
// 對於一個服務器程序來說, 核心流程也是要分成兩步.
// 1. 進行初始化操作 (實例化 Socket 對象)
// 2. 進入主循環, 接受並處理請求. (主循環就是一個 "死循環")
// a) 讀取數據並解析
// b) 根據請求計算響應
// c) 把響應結果寫回到客戶端.
//需要一個udp的連接
private DatagramSocket socket = null;
//map去存儲我們的漢譯英數據
private Map<String, String> map = new HashMap<>();
//在構造函數裏初始化操作實現連接 並指定端口號
public MyUDPServer(int port) throws SocketException {
//添加數據
initDates();
//服務器new socket對象的時候需要和一個ip地址和端口號綁定起來
//如果沒有寫ip 則默認時0.0.0.0 (一個特殊的ip會關聯到這個主機的國有網卡的ip)
//socket對象本省就是一個文件
socket = new DatagramSocket(port);
}
private void initDates() {
map.put("貓", "cat");
map.put("豬", "pig");
map.put("狗", "dog");
map.put("人", "people");
map.put("筆", "pen");
map.put("坐", "sit");
map.put("手", "hand");
map.put("腿", "leg");
}
//進入主循環實現接收 處理 響應
public void start() throws IOException {
System.out.println("服務器啓動");
while (true) {
//接收請求
//這是一個接受數據的緩衝區 地址是接受數據的時候有內存填充
DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
//程序啓動會很快到達receive操作 如果客戶端沒有發送任何數據 此時receive操作會阻塞直到有客戶端發送數據過來
//當整的有哭換端發送過來數據時 receive就會將數據保證到DategramPAcket對象的緩衝區裏
socket.receive(requestPacket);
//原本請求的數據時byte[]需要將其轉換成String 並且如果發來的數據小於我們緩衝區的大小就會默認添加空格 我們得去掉無用空格
String request = new String(requestPacket.getData(), 0, requestPacket.getLength()).trim();
//處理請求
String respond = process(request);
//把響應寫回給客戶端, 響應數據就是 response, 需要包裝成一個 Packet 對象
//此時這個用於send 不僅需要指定緩衝區還不要忘記在Packet對象的最後加上請求數據包裏的Socket地址
//填寫ip和port還可以自己手動設置將ip和port分開寫(如下面案例) 還可以直接定義InetAddress對象(裏面包含ip和port)
DatagramPacket respondPacket = new DatagramPacket(respond.getBytes(),
respond.getBytes().length, requestPacket.getSocketAddress());
socket.send(respondPacket);
//打印請求訪問日誌
System.out.println(requestPacket.getAddress().toString() + " " + requestPacket.getPort() + " request: "
+ request + " respond: " + respond);
}
}
private String process(String request) {
// 由於此處是一個 echo server, 請求內容是啥, 響應內容就是啥.
// 如果是一個更復雜的服務器, 此處就需要包含很多的業務邏輯來進行具體的計算.
return map.getOrDefault(request, "未學習");
}
//一個主函數去設置該服務器的端口 並讓其開始執行
public static void main(String[] args) {
try {
MyUDPServer myUDPServer = new MyUDPServer(9090);
try {
myUDPServer.start();
} catch (IOException e) {
e.printStackTrace();
}
} catch (SocketException e) {
e.printStackTrace();
}
}
}
使用簡單的UDP網絡程序實現客戶端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
//客戶端程序
public class MyUDPClient {
//核心操作有倆步
//啓動客戶端的時候需要指定連接那臺服務器
//執行任務主要流程分4步
// 1. 從用戶這裏讀取輸入的數據.
// 2. 構造請求發送給服務器
// 3. 從服務器讀取響應
// 4. 把響應寫回給客戶端.
//需要客戶端知道要發往哪臺服務器的ip 和端口 還需要一個udp的連接對象
private String severIP = null;
private int severPort = 0;
private DatagramSocket socket = null;
//需要在啓動客戶端的時候來指定需要連接哪個服務器
public MyUDPClient(String severIP, int severPort) throws SocketException {
this.severIP = severIP;
this.severPort = severPort;
//客戶端在創建socket的時候不需要綁定端口號 但是服務器必須綁定端口號
//因爲服務器綁定了端口號 客戶端才能找到去訪問它
//客戶端不綁定是爲了可以在一臺主機上啓動多個客戶端
this.socket = new DatagramSocket();
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while (true) {
//讀取用戶輸入的消息
System.out.print("輸入字符串->");
String request = scanner.nextLine();
if ("exit".equals(request)) {
break;
}
//發送請求
//注意ip和port要分開寫並且前後位置要注意
DatagramPacket requstPacket = new DatagramPacket(request.getBytes(),
request.getBytes().length, InetAddress.getByName(this.severIP), this.severPort);
socket.send(requstPacket);
//接收服務器的響應
DatagramPacket respondPacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(respondPacket);
String respond = new String(respondPacket.getData(), 0, respondPacket.getLength()).trim();
//顯示響應
System.out.println(respond);
}
}
public static void main(String[] args) {
try {
//此時我們用於自己主機實驗 127.0.0.1是一個特殊的ip(環回ip) 自己訪問自己
//如果服務器和客戶端在同一臺主機上舊使用環回ip 如果不在同一臺主機上就必須填寫服務器的ip
//端口號必須與服務器的端口號一致
MyUDPClient client = new MyUDPClient("127.0.0.1", 9090);
try {
client.start();
} catch (IOException e) {
e.printStackTrace();
}
} catch (SocketException e) {
e.printStackTrace();
}
}
}