【Java】網絡編程(Socket)

網絡編程

Socket

我們開發的網絡應用程序位于應用層,TCP和UDP屬于傳輸層協議,在應用層如何使用傳輸層的服務呢?在應用層和傳輸層之間,則使用套接字Socket來進行分離

套接字就像是傳輸層為應用層開的一個小口,應用程序通過這個小口向遠程發送數據,或者接收遠程發來的數據;而這個小口以內,也就是數據進入這個口之后,或者數據從這個口出來之前,是不知道也不需要知道的,也不會關心它如何傳輸,這屬于網絡其他層次工作

Socket實際是傳輸層供給應用層的編程接口。Socket就是應用層與傳輸層之間的橋梁。使用Socket變成可以開發客戶機和服務器的應用程序,可以在本地網絡上通信,也可以通過Internet在全球范圍內通信

Java網絡編程中的常用類

Java為了跨平臺,在網絡應用通信時是不允許直接調用操作系統接口的,而是由java.net包來提供網絡功能。

InetAddress的使用

作用:封裝計算機的IP地址和DNS(沒有端口信息)

特點:

這個類沒有構造方法,如果想要得到對象,只能通過靜態方法:getLocalHost() 、 getByName() 、 getALLByName() 、 getAddress() 、 getHostName()

獲取本機信息

獲取本機信息需要使用getLocalHost()方法創建InetAddress對象,這個對象包含了本機的IP地址,計算機名等信息

public class InetTest {public static void main(String[] args) {try {InetAddress localHost = InetAddress.getLocalHost();String hostAddress = localHost.getHostAddress();String hostName = localHost.getHostName();System.out.println(hostAddress);System.out.println(hostName);} catch (UnknownHostException e) {throw new RuntimeException(e);}}
}

根據域名獲取計算機的信息

根據域名獲取計算機信息時需要使用getByName(“域名”)方法創建InetAddress對象

public class InetTest2 {public static void main(String[] args) {try {InetAddress inetAddress = InetAddress.getByName("www.baidu.com");System.out.println(inetAddress.getHostAddress());System.out.println(inetAddress.getHostName());} catch (UnknownHostException e) {throw new RuntimeException(e);}}
}

根據IP地址獲取計算機的信息

根據IP地址獲取計算機的信息時需要使用getByName(“IP”)方法創建InetAddress對象

public class InetTest3 {public static void main(String[] args) {try {InetAddress inetAddress = InetAddress.getByName("110.242.68.4");System.out.println(inetAddress.getHostName());System.out.println(inetAddress.getHostAddress());} catch (UnknownHostException e) {throw new RuntimeException(e);}}
}
InetSocketAddress的使用

作用:包含IP和端口信息,常用于Socket通信。此類實現IP套接字地址(IP地址+端口號),不依賴任何協議

InetSocketAddress相比較InetAddress多了一個端口號,端口的作用:一臺擁有IP地址的主機可以提供許多服務,比如Web服務、FTP服務、SMTP服務等,這些服務完全可以通過1個IP地址來實現

public class InetSocketTest {public static void main(String[] args) {InetSocketAddress inetSocketAddress = new InetSocketAddress("www.baidu.com",80);System.out.println(inetSocketAddress.getAddress().getHostAddress());System.out.println(inetSocketAddress.getHostName());}
}
URL的使用

IP地址標識了Internet上唯一的計算機,而URL則標識了這些計算機上的資源。URL代表一個資源定位符,它是指向互聯網“資源”的指針。資源可以是簡單的文件或目錄,也可以是對更為復雜的對象的引用,例如對數據庫或者搜索引擎的查詢

為了方便程序員編程,JDK中提供了URL類,該類的全名是java.net.URL,有了這樣一個類,就可以使用它的各種方法來對URL對象進行分割、合并等處理

public class UrlTest {public static void main(String[] args) {try {URL url = new URL("http://www.edu2act.cn/task/list/finished/");System.out.println("獲取當前協議的默認端口:" + url.getDefaultPort());System.out.println("訪問資源:" + url.getFile());System.out.println("主機名" + url.getHost());System.out.println("訪問資源的路徑:" + url.getPath());System.out.println("協議" + url.getProtocol());System.out.println("參數部分" + url.getQuery());} catch (MalformedURLException e) {throw new RuntimeException(e);}}
}

通過URL實現最簡單的網絡爬蟲

public class UrlTest2{public static void main(String[] args)throws Exception {URL url = new URL("http://dbms.wangding.co/design/ch04-database-design-1/");try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {StringBuilder sb = new StringBuilder();String temp;while ((temp = br.readLine()) != null) {sb.append(temp);}System.out.println(sb);} catch (Exception e) {e.printStackTrace();}}
}
TCP通信的實現和項目案例
TCP通信實現原理

前邊我們提到TCP協議是面向的連接的,在通信時客戶端與服務器端必須建立連接。在網絡通訊中,第一次主動發起通訊的程序被稱作客戶端(Client)程序,簡稱客戶端,而在第一次通訊中等待連接的程序被稱作服務器端(Server)程序,簡稱服務器。一旦通訊建立,則客戶端和服務器端完全一樣,沒有本質的區別。

請求-相應 模式

  • Socket類:發送TCP信息
  • ServerSocket類:創建服務器

套接字Socket是一種進程間的數據交換機制。這些進程既可以在同一機器上,也可以在通過網絡連接的不同機器上。換句話說,套接字起到通信端點的作用。單個套接字是一個端點,而一對套接字則構成一個雙向通信信道,使非關聯進程可以在本地或通過網絡進行數據交換。一旦建立套接字連接,數據即可在相同或不同的系統中雙向或單向發送,直到其中一個端點關閉連接。套接字與主機地址和端口地址相關聯。主機地址就是客戶端或服務器程序所在的主機的IP地址。端口地址是指客戶端或服務器程序使用的主機的通信端口。

在客戶端和服務器中,分別創建獨立的Socket,并通過Socket的屬性,將兩個Socket進行連接,這樣,客戶端和服務器通過套接字所建立的連接使用輸入輸出流進行通信。

TCP/IP套接字是最可靠的雙向流協議,使用TCP/IP可以發送任意數量的數據。

實際上,套接字只是計算機上已編號的端口。如果發送方和接收方計算機確定好端口,他們就可以通信了。

客戶端與服務器端的通信關系圖:

在這里插入圖片描述

TCP/IP通信連接的簡單過程

位于A計算機上的TCP/IP軟件向B計算機發送包含端口號的消息,B計算機的TCP/IP軟件接收該消息,并進行檢查,查看是否有它知道的程序正在該端口上接收消息。如果有,他就將該消息交給這個程序。

通過Socket的編程順序

1、創建服務器ServerSocket,在創建時,定義ServerSocket的監聽端口(在這個端口接收客戶端發來的消息)
2、ServerSocket調用accept()方法,使之處于阻塞狀態。
3、創建客戶端Socket,并設置服務器的IP及端口。
4、客戶端發出連接請求,建立連接。
5、分別取得服務器和客戶端Socket的InputStream和OutputStream。
6、利用Socket和ServerSocket進行數據傳輸。
7、 關閉流及Socket。

TCP通信入門案例

創建服務端

public class BasicSocketServer {public static void main(String[] args) throws IOException {System.out.println("服務器已啟動,等待監聽....");ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(8888);Socket socket = serverSocket.accept();//連接成功后會得到與客戶端對應的Socket對象,并解除線程阻塞InputStream in = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(in));System.out.println(br.readLine());OutputStream out = socket.getOutputStream();} catch (IOException e) {throw new RuntimeException(e);}finally {serverSocket.close();}}
}

創建客戶端

public class BasicSocketClient {public static void main(String[] args) throws IOException {Socket socket = null;PrintWriter pw = null;try {socket = new Socket("127.0.0.1",8888);OutputStream out = socket.getOutputStream();pw = new PrintWriter(out);pw.write("服務端,你好!");pw.flush();} catch (IOException e) {throw new RuntimeException(e);}finally {pw.close();socket.close();}}
}
TCP單向通信

單向通信是指通信雙方中,一方固定為發送端,一方固定為接收端

創建服務端

public class OneWaySocketServer {public static void main(String[] args) {System.out.println("服務器啟動,開始監聽");try(ServerSocket serverSocket = new ServerSocket(8888);) {Socket socket = serverSocket.accept();BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter pw = new PrintWriter(socket.getOutputStream());System.out.println("連接成功");while (true){String str = br.readLine();System.out.println("客戶端說" + str);if ("exit".equals(str)){break;}pw.println(str);pw.flush();}} catch (IOException e) {System.out.println("服務器啟動失敗");throw new RuntimeException(e);}}
}

創建客戶端

public class OneWaySocketClient {public static void main(String[] args) {try(Socket socket = new Socket("127.0.0.1",8888)) {Scanner scanner = new Scanner(System.in);PrintWriter pw = new PrintWriter(socket.getOutputStream());BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));while (true){String s = scanner.nextLine();pw.println(s);pw.flush();if ("exit".equals(s)){break;}String serverInput = br.readLine();System.out.println("服務器返回的" + serverInput);}}catch (Exception e){e.printStackTrace();}}
}
TCP雙向通信

雙向通信是指通信雙方中,任何一方都可為發送端,任何一方都可為接收端

服務端

public class TwoWaySocketServer {public static void main(String[] args) {System.out.println("服務器啟動,監聽8888端口");try(ServerSocket serverSocket = new ServerSocket(8888);) {Socket socket = serverSocket.accept();//創建鍵盤輸入對象Scanner scanner = new Scanner(System.in);BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter pw = new PrintWriter(socket.getOutputStream());while (true){String str = br.readLine();System.out.println("客戶端說:" + str);String keyInput = scanner.nextLine();pw.println(keyInput);pw.flush();}}catch (Exception e){e.printStackTrace();}}
}

客戶端

public class TwoWaySocketClient {public static void main(String[] args) {try(Socket socket = new Socket("127.0.0.1",8888)) {Scanner scanner = new Scanner(System.in);BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter pw = new PrintWriter(socket.getOutputStream());while (true){String keyInput = scanner.nextLine();pw.println(keyInput);pw.flush();String str = br.readLine();System.out.println("服務端說:" + str);}}catch (Exception e){e.printStackTrace();}}
}
創建點對點的聊天應用

創建服務端

主線程

public class ChatSocketServer {public static void main(String[] args) {try(ServerSocket serverSocket = new ServerSocket(8888)) {System.out.println("服務端啟動,等待連接");Socket socket = serverSocket.accept();new Thread(new SendThread(socket)).start();new Thread(new ReceiveThread(socket)).start();}catch (Exception e){e.printStackTrace();}}
}

接收消息線程

public class ReceiveThread implements Runnable{private Socket socket;public ReceiveThread(Socket socket){this.socket = socket;}@Overridepublic void run() {this.receiveMsg();}private void receiveMsg(){try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));){while (true){String msg = br.readLine();System.out.println("客戶端說:" + msg);}}catch (Exception e){e.printStackTrace();}}
}

發送消息線程

public class SendThread implements Runnable{private Socket socket;public SendThread(Socket socket){this.socket = socket;}@Overridepublic void run() {this.sendMsg();}private void sendMsg(){try(Scanner scanner = new Scanner(System.in);PrintWriter pw = new PrintWriter(socket.getOutputStream());){while (true){String msg = scanner.nextLine();pw.println(msg);pw.flush();}}catch (Exception e){e.printStackTrace();}}
}

創建客戶端

主線程

public class ChatSocketClient {public static void main(String[] args) {try {Socket socket = new Socket("127.0.0.1",8888);System.out.println("連接成功");new Thread(new ClientSendThread(socket)).start();new Thread(new ClientReceiveThread(socket)).start();}catch (Exception e){e.printStackTrace();}}
}

接收消息線程被

public class ClientReceiveThread implements Runnable{private Socket socket;public ClientReceiveThread(Socket socket){this.socket = socket;}@Overridepublic void run() {receiveMsg();}private void receiveMsg() {try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {while (true){String msg = br.readLine();System.out.println("服務端說:" + msg);}} catch (IOException e) {e.printStackTrace();}}
}

發送消息線程

public class ClientSendThread implements Runnable{private Socket socket;public ClientSendThread(Socket socket){this.socket = socket;}@Overridepublic void run() {this.sendMsg();}private void sendMsg() {try(Scanner scanner = new Scanner(System.in);PrintWriter pw = new PrintWriter(socket.getOutputStream())){while (true){String msg = scanner.nextLine();pw.println(msg);pw.flush();}}catch (Exception e){e.printStackTrace();}}
}
優化點對點的聊天應用
public class GoodTCP {public static void main(String[] args) throws IOException {ServerSocket serverSocket = null;Socket socket = null;try {Scanner scanner = new Scanner(System.in);System.out.println("請輸入:server,<port> 獲取 <ip>,<port>");String str = scanner.nextLine();String[] arr = str.split(",");if("server".equals(arr[0])){System.out.println("TCP Server Listen at" + arr[1] + "......");serverSocket = new ServerSocket(Integer.parseInt(arr[1]));socket = serverSocket.accept();new Receive(socket);}else {socket = new Socket(arr[0],Integer.parseInt(arr[1]));System.out.println("連接成功");}new Thread(new Send(socket, scanner)).start();new Thread(new Receive(socket)).start();}catch (Exception e){e.printStackTrace();}finally {if(serverSocket != null){serverSocket.close();}}}
}
public class Send implements Runnable{private Socket socket;private Scanner scanner;public Send(Socket socket, Scanner scanner) {this.socket = socket;this.scanner = scanner;}@Overridepublic void run() {this.sendMsg();}private void sendMsg(){try(PrintWriter pw = new PrintWriter(socket.getOutputStream());){while (true){String msg = scanner.nextLine();pw.println(msg);pw.flush();}}catch (Exception e){e.printStackTrace();}}
}
public class Receive implements Runnable{private Socket socket;public Receive(Socket socket){this.socket = socket;}@Overridepublic void run() {sendMsg();}private void sendMsg(){try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));){while (true){String msg = br.readLine();System.out.println("客戶端說:" + msg);}}catch (Exception e){e.printStackTrace();}}
}
一對多應用

服務端應該將serverSocket.accept()放入while(true)循環中

一對多聊天服務器設計

難點在于解決線程同步

當沒有消息發送時,發送線程處于等待狀態,當接收線程接收到消息后,喚醒所有等待的發送線程

public class ChatRoomServer {public static String buf;public static void main(String[] args) {System.out.println("Chat Server Version 1.0");System.out.println("Listen at 8888......");try(ServerSocket serverSocket = new ServerSocket(8888)){while (true){Socket socket = serverSocket.accept();System.out.println("連接到" + socket.getInetAddress());new Thread(new ChatReceiveThread(socket)).start();new Thread(new ChatSendThread(socket)).start();}}catch (Exception e){e.printStackTrace();}}
}
public class ChatReceiveThread implements Runnable {private Socket socket;public ChatReceiveThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {receiveMsg();}private void receiveMsg() {try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));){while (true){String msg = br.readLine();synchronized ("abc"){ChatRoomServer.buf = "[" + this.socket.getInetAddress() + "]" + msg;"abc".notifyAll();}}}catch (Exception e){e.printStackTrace();}}
}
public class ChatSendThread implements Runnable{Socket socket;public ChatSendThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {sendMsg();}private void sendMsg() {try(PrintWriter pw = new PrintWriter(socket.getOutputStream());){while (true){synchronized ("abc"){//先讓發送消息的線程處于等待狀態"abc".wait();//將公共數據區的數據發送給客戶端pw.println(ChatRoomServer.buf);pw.flush();}}}catch (Exception e){e.printStackTrace();}}
}
UDP通信實現原理

UDP協議與之前講到的TCP協議不同,是面向無連接的,雙方不需要建立連接便可通信。UDP通信所發送的數據需要進行封包操作(使用DatagramPacket類),然后才能接收或發送(使用DatagramSocket類)。

DatagramPacket:數據容器(封包)的作用

此類表示數據報包。 數據報包用來實現封包的功能。

常用方法

方法名使用說明
DatagramPacket(byte[] buf, int length)構造數據報包,用來接收長度為 length 的數據包
DatagramPacket(byte[] buf, int length, InetAddress address, int port)構造數據報包,用來將長度為 length 的包發送到指定主機上的指定端口號
getAddress()獲取發送或接收方計算機的IP地址,此數據報將要發往該機器或者是從該機器接收到的
getData()獲取發送或接收的數據
setData(byte[] buf)設置發送的數據

DatagramSocket:用于發送或接收數據報包

當服務器要向客戶端發送數據時,需要在服務器端產生一個DatagramSocket對象,在客戶端產生一個DatagramSocket對象。服務器端的DatagramSocket將DatagramPacket發送到網絡上,然后被客戶端的DatagramSocket接收。

DatagramSocket有兩種常用的構造函數。一種是無需任何參數的,常用于客戶端;另一種需要指定端口,常用于服務器端。如下所示:

  • DatagramSocket() :構造數據報套接字并將其綁定到本地主機上任何可用的端口。
  • DatagramSocket(int port) :創建數據報套接字并將其綁定到本地主機上的指定端口。

常用方法

方法名使用說明
send(DatagramPacket p)從此套接字發送數據報包
receive(DatagramPacket p)從此套接字接收數據報包
close()關閉此數據報套接字

UDP通信編程基本步驟:

1、創建客戶端的DatagramSocket,創建時,定義客戶端的監聽端口。
2、創建服務器端的DatagramSocket,創建時,定義服務器端的監聽端口。
3、在服務器端定義DatagramPacket對象,封裝待發送的數據包。
4、客戶端將數據報包發送出去。
5、服務器端接收數據報包。

UDP通信入門案例

服務端

public class UDPServer {public static void main(String[] args) {//創建服務端接收數據的DatagramSocket對象try(DatagramSocket datagramSocket = new DatagramSocket(9999)){//創建數據緩沖區byte[] b = new byte[1024];//創建數據報包對象DatagramPacket datagramPacket = new DatagramPacket(b,b.length);//等待接收客戶端所發送的數據datagramSocket.receive(datagramPacket);//取出數據String str = new String(datagramPacket.getData(),0,datagramPacket.getLength());System.out.println(str);}catch (Exception e){e.printStackTrace();}}
}

客戶端

public class UDPClient {public static void main(String[] args) {try(DatagramSocket datagramSocket = new DatagramSocket(8888);) {byte[] bytes = "Triticale".getBytes();DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,new InetSocketAddress("127.0.0.1",9999));datagramSocket.send(datagramPacket);}catch (Exception e){e.printStackTrace();}}
}
基本數據類型通信

服務端

try(DataInputStream dis = new DataInputStream(new ByteArrayInputStream(datagramPacket.getData()));){System.out.println(dis.readLong());
}

客戶端

long n = 2000l;
try(ByteArrayOutputStream bos = new ByteArrayOutputStream();DataOutputStream dos = new DataOutputStream(bos);){dos.writeLong(n);byte[] arr = bos.toByteArray();DatagramPacket datagramPacket1 = new DatagramPacket(arr,arr.length,new InetSocketAddress("127.0.0.1",9999));
}
傳遞自定義數據類型

創建服務端

public class ObjectTypeUDPServer {public static void main(String[] args) {try(DatagramSocket datagramSocket = new DatagramSocket(9999)){byte[] bytes = new byte[1024];DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);datagramSocket.receive(datagramPacket);try(ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(datagramPacket.getData()))){Person person = (Person) ois.readObject();System.out.println(person.toString());}}catch (Exception e){e.printStackTrace();}}
}

創建客戶端

public class ObjectTypeUDPClient {public static void main(String[] args) {try(DatagramSocket datagramSocket = new DatagramSocket(8888);ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos)){Person person = new Person();person.setName("張三");person.setAge(18);oos.writeObject(person);byte[] byteArray = bos.toByteArray();DatagramPacket datagramPacket = new DatagramPacket(byteArray,byteArray.length,new InetSocketAddress("127.0.0.1",9999));datagramSocket.send(datagramPacket);}catch (Exception e){e.printStackTrace();}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81361.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81361.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81361.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【教程】Docker方式本地部署Overleaf

轉載請注明出處&#xff1a;小鋒學長生活大爆炸[xfxuezhagn.cn] 如果本文幫助到了你&#xff0c;歡迎[點贊、收藏、關注]哦~ 目錄 背景說明 下載倉庫 初始化配置 修改監聽IP和端口 自定義網站名稱 修改數據存放位置 更換Docker源 更換Docker存儲位置 啟動Overleaf 創…

根據用戶ID獲取所有子節點數據或是上級直屬節點數據

一、根據用戶ID獲取所有子節點&#xff0c;通過存儲過程來實現 CREATE DEFINERcrmeb% PROCEDURE proc_get_user_all_children( IN rootUid INTEGER, -- 要查詢的根用戶ID IN includeSelf BOOLEAN -- 是否包含自身(1包含,0不包含) ) BEGIN -- 聲明變…

計算機組成原理——數據的表示

2.1數據的表示 整理自Beokayy_ 1.進制轉換 十六進制與二進制的轉換 一位十六進制等于四位二進制 四位二進制等于一位十六進制 0x173A4C0001 0111 0011 1010 0100 1100 十六進制與十進制的轉換 十六轉十&#xff1a;每一位數字乘以相應的16的冪再相加 十轉十六&#xff1a…

基于MATLAB-GUI圖形界面的數字圖像處理

基于MATLAB GUI的數字圖像處理系統實現方案&#xff0c;包含常見圖像處理功能。代碼分為兩部分&#xff1a;GUI界面設計和回調函數實現。 %% 第一部分&#xff1a;創建GUI界面 (使用GUIDE) % 1. 打開GUIDE: guide % 2. 創建新GUI&#xff0c;添加以下控件&#xff1a; % - …

從裸機開發到實時操作系統:FreeRTOS詳解與實戰指南

從裸機開發到實時操作系統&#xff1a;FreeRTOS詳解與實戰指南 本文將帶你從零開始&#xff0c;深入理解嵌入式系統中的裸機開發與實時操作系統&#xff0c;以FreeRTOS為例&#xff0c;全面剖析其核心概念、工作原理及應用場景。無論你是嵌入式新手還是希望提升技能的開發者&am…

zabbix7.2最新版本 nginx自定義監控(三) 設置觸發器

安裝zabbix-get服務 在zabbix-server端口安裝zabbix-get服務 [rootlocalhost ~]# dnf install -y zabbix-get Last metadata expiration check: 1:55:49 ago on Wed 14 May 2025 09:24:49 AM CST. Dependencies resolved. Package Architectur…

在 Kotlin 中,什么是解構,如何使用?

在 Kotlin 中&#xff0c;解構是一種語法糖&#xff0c;允許將一個對象分解為多個獨立的變量。 這種特性可以讓代碼更簡潔、易讀&#xff0c;尤其適用于處理數據類、集合&#xff08;如 Pair、Map&#xff09;或其他結構化數據。 1 解構的核心概念 解構通過定義 componentN()…

html的鼠標點擊事件有哪些寫法

在HTML中&#xff0c;鼠標點擊事件的實現方式多樣&#xff0c;以下從基礎語法到現代實踐為您詳細梳理&#xff1a; 一、基礎寫法&#xff1a;直接內聯事件屬性 在HTML標簽內通過on前綴事件屬性綁定處理函數&#xff0c;適合簡單交互場景&#xff1a; <!-- 單擊事件 -->…

基于EFISH-SCB-RK3576/SAIL-RK3576的智能垃圾分類站技術方案

&#xff08;國產化替代J1900的環保物聯網解決方案&#xff09; 一、硬件架構設計? ?多模態感知系統? ?高精度識別模塊?&#xff1a; 雙光譜成像&#xff08;RGB近紅外&#xff09;融合NPU加速ResNet50模型&#xff0c;支持40垃圾品類識別&#xff08;準確率>99.5%&am…

PYTHON訓練營DAY27

裝飾器 編寫一個裝飾器 logger&#xff0c;在函數執行前后打印日志信息&#xff08;如函數名、參數、返回值&#xff09; logger def multiply(a, b):return a * bmultiply(2, 3) # 輸出: # 開始執行函數 multiply&#xff0c;參數: (2, 3), {} # 函數 multiply 執行完畢&a…

Android Studio 中 build、assemble、assembleDebug 和 assembleRelease 構建 aar 的區別

上一篇&#xff1a;Tasks中沒有build選項的解決辦法 概述&#xff1a; 在構建 aar 包時通常會在下面的選項中進行構建&#xff0c;但是對于如何構建&#xff0c;選擇哪種方式構建我還是處于懵逼狀態&#xff0c;所以我整理了一下幾種構建方式的區別以及如何選擇。 1. build…

視頻質量分析時,遇到不同分辨率的對照視頻和源視頻,分辨率對齊的正確順序。

背景 我們平時在做視頻轉碼后&#xff0c;會用VMAF/PSNR得評分工具進行視頻對比的評分&#xff0c;但是這幾種客觀評分方式都有一個要求就是分辨率要一模一樣&#xff0c;因為這樣才對像素點做數學運算。 但是分辨率對齊其實有兩種選擇&#xff0c;例如源視頻是1080P&#xf…

【技巧】離線安裝docker鏡像的方法

回到目錄 【技巧】離線安裝docker鏡像的方法 0. 為什么需要離線安裝&#xff1f; 第一、 由于docker hub被墻&#xff0c;所以 拉取鏡像需要配置國內鏡像源 第二、有一些特殊行業服務器無法接入互聯網&#xff0c;需要手工安裝鏡像 1. 可以正常拉取鏡像服務器操作 服務器…

計算機網絡 : 網絡基礎

計算機網絡 &#xff1a; 網絡基礎 目錄 計算機網絡 &#xff1a; 網絡基礎引言1. 網絡發展背景2. 初始協議2.1 初始協議2.2 協議分層2.2.1 軟件分層的好處2.2.2 OSI七層模型2.2.3 TCP/IP五層&#xff08;四層&#xff09;模型 2.3 TCP/IP協議2.3.1TCP/IP協議與操作系統的關系&…

【2025最新】Windows系統裝VSCode搭建C/C++開發環境(附帶所有安裝包)

文章目錄 為什么選擇VSCode作為C/C開發工具&#xff1f;一、VSCode安裝過程&#xff08;超簡單&#xff01;&#xff09;二、VSCode中文界面設置&#xff08;再也不用對著英文發愁&#xff01;&#xff09;三、安裝C/C插件&#xff08;編程必備神器&#xff01;&#xff09;四、…

Jmeter 安裝包與界面漢化

Jmeter 安裝包&#xff1a; 通過網盤分享的文件&#xff1a;CSDN-apache-jmeter-5.5 鏈接: https://pan.baidu.com/s/17gK98NxS19oKmkdRhGepBA?pwd1234 提取碼: 1234 Jmeter界面漢化&#xff1a;

HandlerInterceptor介紹-筆記

1. HandlerInterceptor簡介 org.springframework.web.servlet.HandlerInterceptor 是 Spring MVC 中用于攔截 HTTP 請求的核心接口。 public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object ha…

C++循環效率比較與優化建議

在 C++ 中,不同循環結構(如 for、while、do-while、基于范圍的 for)在優化后的性能通常是等效的,因為現代編譯器會對它們進行底層優化,生成相似的機器代碼。循環的效率更多取決于循環體內的操作和數據訪問模式,而非循環結構本身的選擇。以下是關鍵點總結: 1. 傳統循環的…

北京孫河傲云源墅:限量典藏的主城墅居臻品

在限墅令的背景下&#xff0c;北京主城的墅居產品日益稀缺&#xff0c;而傲云源墅作為孫河墅區的杰出之作&#xff0c;憑借其獨特的價值&#xff0c;成為了眾多高端置業者的理想選擇。 傲云源墅所處的孫河地區&#xff0c;是北京公認的高價值板塊。其地位在 2025 年孫河 2902 …

簡單入門RabbitMQ

本章將帶大家來寫一個簡單的程序&#xff0c;使用 Java 創建RabbitMQ 的生產者和消費者 依賴引入 在 Maven 倉庫中輸入 amqp-client&#xff1a; 找到第一個 RabbitMQ Java Client &#xff0c;點擊進去找到一個合適的版本然后將依賴引入到我們項目中的 pom.xml 文件中。 …