用TCP實現服務器與客戶端的交互

目錄

?一、TCP的特點

二、API介紹?

1.ServerSocket

?2.Socket

三、實現服務器

四、實現客戶端

五、測試解決bug

1.客戶端發送了數據之后,并沒有響應

2.clientSocket沒有執行close()操作

3.嘗試使用多個客戶端同時連接服務器

六、優化

1.短時間有大量客戶端訪問并斷開連接

2.有大量的客戶端長時間在線訪問

七、源碼


引言:

這篇文章主要是用TCP構造的回顯服務器,也就是客戶端發什么,就返回什么。用實現這個過程方式來學會TCP套接字的使用。

?一、TCP的特點

  • TCP是可靠的:這個需要去了解TCP的機制,這是一個大工程,博主后面寫好了把連接附上
  • TCP是面向字節流
  • TCP是全雙工
  • TCP是有連接

除了可靠性,在編程中無法體會到,其他特性我都會一 一講解。

二、API介紹?

1.ServerSocket

ServerSocket 是創建TCP服務端Socket的API

ServerSocket 構造?法:

方法簽名方法說明
ServerSocket(int port)創建一個服務端流套接字Socket,并綁定到指定端口

ServerSocket ?法:
?法簽名
?法說明
Socket accpet()開始監聽指定端口(創建時綁定端口),有客戶端連接后,返回一個服務端Socket對象,并基于Socket建立與客戶端的連接,否則阻塞等待
void close()

關閉此套接字

?2.Socket

Socket 是客?端Socket,或服務端中接收到客?端建?連接(accept?法)的請求后,返回的服務端Socket。
不管是客?端還是服務端Socket,都是雙?建?連接以后,保存的對方信息,及?來與對?收發數據的

?Socket構造方法:

方法簽名方法說明
Socket(String host, int port)

創建一個客戶端流套接字Socket,并與對應IP的主機上,對應端口的進程建立連接。

host:IP地址

prot:端口號

?這里new出來就是和對方建立完成了。如果建立失敗,就會在構造對象的時候拋出異常。

Socket方法:

方法簽名方法說明
InetAddress getInetAddress()
返回套接字所連接的地址
InputStream getInputStream()
返回此套接字的輸?流
OutputStream getOutputStream()
返回此套接字的輸出流

三、實現服務器

服務器需要指定端口號:

public class TcpEchoServer {private ServerSocket serverSocket = null;//               需要指定服務器的端口    處理ServerSocket拋出的異常public TcpEchoServer(int port) throws IOException {//                     指定服務器的端口serverSocket = new ServerSocket(port);}
}

注意處理拋出的異常

和客戶端建立連接:

public class TcpEchoServer {private ServerSocket serverSocket = null;//               需要指定服務器的端口    處理ServerSocket拋出的異常public TcpEchoServer(int port) throws IOException {//                     指定服務器的端口serverSocket = new ServerSocket(port);}public void start() throws IOException {//服務器需要不停的執行while (true) {//開始監聽指定端口,當有客戶端連接后,返回一個保存對方信息的SocketSocket clientSocket = serverSocket.accept();//處理邏輯processConnection(clientSocket);}}//針對一個連接,提供處理邏輯private void processConnection(Socket clientSocket) {}
}

這里的accept()就體現了TCP的有連接

當連接成功后,需要處理的邏輯:

    //針對一個連接,提供處理邏輯private void processConnection(Socket clientSocket) {//打印客戶端的信息                               返回IP地址                      返回端口號System.out.printf("[%s : %d]客戶端上線\n",clientSocket.getInetAddress(), clientSocket.getPort());//獲取到socket中持有的流對象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {//1.獲取請求//2.處理請求//3.返回響應//4.打印日志}}catch (IOException e) {}}

全雙工的意思:通信的雙方(如客戶端和服務器)可以在同一時間內同時進行數據的發送和接收,即兩個方向的數據流可以同時傳輸,互不干擾。

這里的getInputStream、getOutputStream就體現了全雙工和面向字節流。

不了解這兩個接口的可以去看我這篇文章:

JAVA如何操作文件?(超級詳細)_java操作文件-CSDN博客

實現處理邏輯:

    //針對一個連接,提供處理邏輯private void processConnection(Socket clientSocket) {//打印客戶端的信息                               返回IP地址                      返回端口號System.out.printf("[%s : %d]客戶端上線\n",clientSocket.getInetAddress(), clientSocket.getPort());//獲取到socket中持有的流對象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {//因為我們用字符串來做為數據傳輸,用Scanner就可以更方便的傳輸了Scanner scanner = new Scanner(inputStream);//包裝輸出流,主要是用println()會在數據之后加上\nPrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.獲取請求if (!scanner.hasNext()) {//如果scanner無法讀取出數據,說明客戶端斷開了連接,導致服務器這邊讀取到”末尾“break;}//2.處理請求//接收客戶端的請求//如果遇到 空白字符 就會停止輸入String request = scanner.next();//處理請求String response = process(request);//3.返回響應//此處可以按字節數組的形式,但是我們要輸入的是字符串,這個就不太方便//outputStream.write(response.getBytes());//此方法在寫入之后會自動加上\nprintWriter.println(response);//4.打印日志System.out.printf("[%s : %d] 請求 = %s 響應 = %s \n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e) {throw new RuntimeException();}}private String process(String request) {//由于我們是回顯服務器這里直接返回就可以了return request;}

注意里面使用了兩個接口包裝了一下輸入輸出流,最主要的是可以在用\n做為分割。

注意里面的:

發送字符串給客戶端,最后會自動加上 \n 做為結尾

println(response);

接收客戶端信息,以空白符做為結尾。

空白符:包括不限于 空格、回車、制表符……

scanner.next();

如果是nextLine()就比較嚴格,必須是\n做為結尾

這里的服務器處理邏輯就寫完了,但其實這里還有三個錯誤,后面再單獨講解:

public class TcpEchoServer {private ServerSocket serverSocket = null;//               需要指定服務器的端口    處理ServerSocket拋出的異常public TcpEchoServer(int port) throws IOException {//                     指定服務器的端口serverSocket = new ServerSocket(port);}public void start() throws IOException {//服務器需要不停的執行while (true) {//開始監聽指定端口,當有客戶端連接后,返回一個保存對方信息的SocketSocket clientSocket = serverSocket.accept();//處理邏輯processConnection(clientSocket);}}//針對一個連接,提供處理邏輯private void processConnection(Socket clientSocket) {//打印客戶端的信息                               返回IP地址                      返回端口號System.out.printf("[%s : %d]客戶端上線\n",clientSocket.getInetAddress(), clientSocket.getPort());//獲取到socket中持有的流對象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {//因為我們用字符串來做為數據傳輸,用Scanner就可以更方便的傳輸了Scanner scanner = new Scanner(inputStream);//包裝輸出流,主要是用println()會在數據之后加上\nPrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.獲取請求if (!scanner.hasNext()) {//如果scanner無法讀取出數據,說明客戶端斷開了連接,導致服務器這邊讀取到”末尾“break;}//2.處理請求//接收客戶端的請求//如果遇到 空白字符 就會停止輸入String request = scanner.next();//處理請求String response = process(request);//3.返回響應//此處可以按字節數組的形式,但是我們要輸入的是字符串,這個就不太方便//outputStream.write(response.getBytes());//此方法在寫入之后會自動加上\nprintWriter.println(response);//4.打印日志System.out.printf("[%s : %d] 請求 = %s 響應 = %s \n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e) {throw new RuntimeException();}}private String process(String request) {//由于我們是回顯服務器這里直接返回就可以了return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(8080);server.start();}
}

四、實現客戶端

指定服務器的IP和端口號:

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//這里只要建立實例,就是和服務端的accept()建立了連接//socket也就保存了服務器的IP和端口號等//需要傳入服務器的 IP地址 和 端口號socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客戶端啟動!");}}

整體邏輯:

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//需要傳入服務器的 IP地址 和 端口號//這里只要建立實例,就是和服務端的accept()建立了連接socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客戶端啟動!");try(OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream()) {while (true) {//1.從控制臺獲取數據//2.將數據發送給服務器//3.接收服務器響應//4.打印相關結果}}catch (IOException e) {throw new RuntimeException();}}
}

整體邏輯實現:

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//這里只要建立實例,就是和服務端的accept()建立了連接//socket也就保存了服務器的IP和端口號等//需要傳入服務器的 IP地址 和 端口號socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客戶端啟動!");try(OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream()) {//用來接收服務器的信息Scanner scanner = new Scanner(inputStream);//用于接收用戶輸入Scanner scannerIn = new Scanner(System.in);//用于輸出數據給服務器PrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.從控制臺獲取數據System.out.print("->");String request = scannerIn.next();//2.將數據發送給服務器printWriter.println(request);//3.接收服務器響應//判斷服務端是否還有信息if (!scanner.hasNext()) {break;}//接收服務端信息String response = scanner.next();//4.打印相關結果System.out.println(response);}}catch (IOException e) {throw new RuntimeException();}}public static void main(String[] args) throws IOException {
//                                              127.0.0.1是專門用來訪問自己的TcpEchoClient client = new TcpEchoClient("127.0.0.1",8080);client.start();}
}

這里仍然純在一個問題,一會和服務器的問題一起將

五、測試解決bug

最后我會把所有的問題解決了,再把源附上

1.客戶端發送了數據之后,并沒有響應

?先運行服務器,再運行客戶端

可以看到目前還是成功的,那么我們來輸入數據。

我們在客戶端輸入了消息,但是沒有任何反應了!

此處的情況是,客戶端并沒有真正把請求發出去:

PrintWriter這樣的類,以及很多IO流中的類,都是 “自帶緩沖區” 的。

此方法就帶有緩沖區:

printWriter.println(request);

引入緩沖區之后,進行寫入數據的操作,并不會馬上觸發IO,而是先放到內存緩沖區中,等到緩沖區里攢了一波之后,再統一進行發送。

為什么引入緩沖區的機制?

因為IO操作其實是不小的開銷,如果數據量較少,那么每一次都進行IO,就有很大一部分開銷是IO操作。如果積累到一定數據量再進行IO操作,那么一次IO就傳輸了這么多數據。

我們可以使用flush方法,主動“刷新緩沖區”:

注意:

服務器 和 客戶端 都需要在printWriter.println();后面加上flush()方法。

再來測試:

此時就可以接收到了

2.clientSocket沒有執行close()操作

這個問題比較隱蔽,這些ServerSocket 和 Socket 每一次都會在“文件描述符”中創建一個新的表項。

文件描述符:描述了該進程都要操作哪些文件。數組的每個元素就是一個struct file對象,每個結構體就描述了對應的文件信息,數組的小標就稱為“文件描述符”。

每次打開一個文件,就想當于在數組上占用了一個位置,而這個數組又是不能擴容的,如果數組滿了就會打開文件失敗。除非主動調用close才會關閉文件,或者這個進程直接結束了這個數組也被帶走了。

那么我們就需要處理一下clientSocket:

?3.嘗試使用多個客戶端同時連接服務器

要對同一代碼啟動多個進程,需要設置一下步驟:

分別啟動客戶端1 和 客戶端2 ,可以看到服務器上根本沒有第二個客戶端啟動的信息:

原因:

我們可以用多線程去執行專門執行每一個客戶端的請求:

    public void start() throws IOException {//服務器需要不停的執行while (true) {//開始監聽指定端口,當有客戶端連接后,返回一個保存對方信息的SocketSocket clientSocket = serverSocket.accept();//讓一個線程去對應一個客戶端Thread thread = new Thread(() -> {//處理邏輯processConnection(clientSocket);});thread.start();}}

結果:

bug問題解決了,但還有一些場景,可能會把服務器干崩潰

六、優化

1.短時間有大量客戶端訪問并斷開連接

一旦短時間內有大量的客戶端,并且每個客戶端請求都是很快的連接之后并退出的,這個時候對于服務器來說,就會有比較大的壓力。這個時候,就算是進程比線程更加的輕量,但是短時間內有大量的線程創建銷毀,就無法忽略它的開銷了。

我們可以引入線程池,這樣就解決了這個問題:

    public void start() throws IOException {//服務器需要不停的執行while (true) {//開始監聽指定端口,當有客戶端連接后,返回一個保存對方信息的SocketSocket clientSocket = serverSocket.accept();ExecutorService service = Executors.newCachedThreadPool();//            //讓一個線程去對應一個客戶端
//            Thread thread = new Thread(() -> {
//                //處理邏輯
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            thread.start();service.submit(() -> {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}});}}

這個線程可創建的線程數是很大的:

2.有大量的客戶端長時間在線訪問

例如直播這樣的情況,每個客戶端分配一個線程,對于一個系統來說,這里搞幾百個線程壓力就非常大了。所以這里 線程池/線程 都不太適用了。

可以使用 IO多路復用 ,也就是一個線程分配多個客戶端進行服務,因為大部分時間線程都是在等待狀態,就能夠讓線程分配多個客戶端,這樣的機制我們做為java程序員不需要過多了解,這樣的機制以及被大佬們,裝進各種框架中了。

七、源碼

服務器源碼:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TcpEchoServer {private ServerSocket serverSocket = null;//               需要指定服務器的端口    處理ServerSocket拋出的異常public TcpEchoServer(int port) throws IOException {//                     指定服務器的端口serverSocket = new ServerSocket(port);}public void start() throws IOException {//服務器需要不停的執行while (true) {//開始監聽指定端口,當有客戶端連接后,返回一個保存對方信息的SocketSocket clientSocket = serverSocket.accept();ExecutorService service = Executors.newCachedThreadPool();//            //讓一個線程去對應一個客戶端
//            Thread thread = new Thread(() -> {
//                //處理邏輯
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            thread.start();service.submit(() -> {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}});}}//針對一個連接,提供處理邏輯private void processConnection(Socket clientSocket) throws IOException {//打印客戶端的信息                               返回IP地址                      返回端口號System.out.printf("[%s : %d]客戶端上線\n",clientSocket.getInetAddress(), clientSocket.getPort());//獲取到socket中持有的流對象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {//因為我們用字符串來做為數據傳輸,用Scanner就可以更方便的傳輸了Scanner scanner = new Scanner(inputStream);//包裝輸出流,主要是用println()會在數據之后加上\nPrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.獲取請求if (!scanner.hasNext()) {//如果scanner無法讀取出數據,說明客戶端斷開了連接,導致服務器這邊讀取到”末尾“break;}//2.處理請求//接收客戶端的請求//如果遇到 空白字符 就會停止輸入String request = scanner.next();//處理請求String response = process(request);//3.返回響應//此處可以按字節數組的形式,但是我們要輸入的是字符串,這個就不太方便//outputStream.write(response.getBytes());//此方法在寫入之后會自動加上\nprintWriter.println(response);//刷新緩沖區printWriter.flush();//4.打印日志System.out.printf("[%s : %d] 請求 = %s 響應 = %s \n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e) {throw new RuntimeException();} finally {System.out.printf("[%s : %d]客戶端下線\n",clientSocket.getInetAddress(), clientSocket.getPort());clientSocket.close();}}private String process(String request) {//由于我們是回顯服務器這里直接返回就可以了return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(8080);server.start();}
}

客戶端源碼:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//這里只要建立實例,就是和服務端的accept()建立了連接//socket也就保存了服務器的IP和端口號等//需要傳入服務器的 IP地址 和 端口號socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客戶端啟動!");try(OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream()) {//用來接收服務器的信息Scanner scanner = new Scanner(inputStream);//用于接收用戶輸入Scanner scannerIn = new Scanner(System.in);//用于輸出數據給服務器PrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.從控制臺獲取數據System.out.print("->");String request = scannerIn.next();//2.將數據發送給服務器printWriter.println(request);//刷新緩沖區printWriter.flush();//3.接收服務器響應//判斷服務端是否還有信息if (!scanner.hasNext()) {break;}//接收服務端信息String response = scanner.next();//4.打印相關結果System.out.println(response);}}catch (IOException e) {throw new RuntimeException();}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1",8080);client.start();}
}

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

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

相關文章

鳥籠效應——AI與思維模型【84】

一、定義 鳥籠效應思維模型指的是人們在偶然獲得一件原本不需要的物品后,會為了這件物品的配套或使用需求,進而繼續添加更多與之相關但自己原本可能并不需要的東西,仿佛被這個“鳥籠”牽著走,最終陷入一種慣性消費或行為模式的現象。簡單來說,就是人們在心理上會有一種自…

加密解密記錄

一、RSA 加密解密 密鑰對生成 1.前端加密解密 (1).vue頁面引入 npm install jsencrypt(2)工具 jsencrypt.js import JSEncrypt from jsencrypt/bin/jsencrypt.min// 密鑰對生成 http://web.chacuo.net/netrsakeypairconst p…

淺析 MegEngine 對 DTR 的實現與改進

分享筆者在學習 MegEngine 對 DTR 的實現時的筆記。關于 DTR 可以參考:【翻譯】DTR_ICLR 2021 文章目錄 MegEngine 架構設計MegEngine 的動態圖部分Imperative RuntimeImperative 與 MegDNN / MegBrain 的關系靜態圖運行時管家 —— MegBrain動態圖接口 —— Impera…

micro-app前端微服務原理解析

一、核心設計思想 基于 WebComponents 的組件化渲染 micro-app 借鑒 WebComponents 的 CustomElement 和 ShadowDom 特性&#xff0c;將子應用封裝為類似 WebComponent 的自定義標簽&#xff08;如 <micro-app>&#xff09;。通過 ShadowDom 的天然隔離機制&#xff0c;實…

CMake中強制啟用option定義變量的方法

在CMake中&#xff0c;若要在另一個CMake文件中強制啟用由option()定義的變量&#xff0c;可使用set(... FORCE)覆蓋緩存變量。具體步驟如下&#xff1a; 使用set命令強制覆蓋緩存&#xff1a; 在需要強制啟用選項的CMake文件中&#xff0c;使用set命令并指定CACHE和FORCE參數。…

C++漫溯鍵值的長河:map set

文章目錄 1.關聯式容器2.set2.1 find2.2 lower_bound、upper_bound 3.multiset3.1 count3.2 equal_range 4.map4.1 insert4.2 operate->4.3 operate[ ]4.4 map的應用實踐&#xff1a;隨機鏈表的復制 5.multimap希望讀者們多多三連支持小編會繼續更新你們的鼓勵就是我前進的動…

汽車用品商城小程序源碼介紹

基于ThinkPHPFastAdminUniApp開發的汽車用品商城小程序源碼&#xff0c;從技術架構來看&#xff0c;ThinkPHP作為后端框架&#xff0c;提供了穩定且高效的開發基礎&#xff0c;能夠處理復雜的業務邏輯和數據交互。FastAdmin則進一步簡化了后臺管理系統的開發流程&#xff0c;提…

力扣hot100——114.二叉樹展開為鏈表

基于 Morris 遍歷思想 將左子樹插到右子樹的位置&#xff0c;將原來的右子樹插到左子樹的最右結點&#xff0c;遍歷右結點重復以上步驟&#xff0c;直至右結點為空。 class Solution { public:void flatten(TreeNode* root) {if(rootnullptr) return;while(root){if(!root-&g…

JConsole監控centos服務器中的springboot的服務

場景 在centos服務器中,有一個aa.jar的springboot服務,我想用JConsole監控它的JVM情況,具體怎么實現。 配置 Spring Boot 應用以啟用 JMX 在java應用啟動項進行配置 java -Djava.rmi.server.hostname=服務器IP -Dcom.sun.management.jmxremote=true \ -Dcom.sun.managem…

39.RocketMQ高性能核心原理與源碼架構剖析

1. 源碼環境搭建 1.1 主要功能模塊 ? RocketMQ的官方Git倉庫地址&#xff1a;GitHub - apache/rocketmq: Apache RocketMQ is a cloud native messaging and streaming platform, making it simple to build event-driven applications. ? RocketMQ的官方網站上下載指定版…

施磊老師rpc(一)

文章目錄 mprpc項目**項目概述**&#xff1a;深入學習到什么**前置學習建議**&#xff1a;核心內容其他技術與工具**項目特點與要求**&#xff1a;**環境準備**&#xff1a; 技術棧集群和分布式理論單機聊天服務器案例分析集群聊天服務器分析分布式系統介紹多個模塊的局限引入分…

基于LangChain構建最小智能體(Agent)實現指南

摘要 本文完整解析基于LangChain的極簡Agent實現方案&#xff0c;通過26行代碼構建具備網絡搜索能力的對話系統&#xff0c;涵蓋Agent初始化、工具集成、流式回調等核心技術要點。適用于LLM應用開發者快速入門Agent開發。(參考項目代碼&#xff1a;Minimal Agent) 系統架構設計…

AWTK:一鍵切換皮膚,打造個性化UI

想讓你的應用在不同場景下都能完美呈現嗎&#xff1f;皮膚切換功能必不可少&#xff01;本文將介紹AWTK&#xff0c;一款強大的GUI框架&#xff0c;它通過內置資源管理和優化緩存&#xff0c;輕松實現皮膚切換功能。 前言 當今的UI應用中&#xff0c;為了滿足不同使用場景和…

【Vagrant+VirtualBox創建自動化虛擬環境】Ansible測試Playbook

文章目錄 Vagrant安裝vagrant安裝 VirtualBox如何使用 Ansible安裝AnsiblePlaybook測試創建hosts文件創建setup.yml文件 Vagrant Vagrant是一個基于Ruby的工具&#xff0c;用于創建和部署虛擬化開發環境。它使用Oracle的開源VirtualBox虛擬化系統&#xff0c;使用 Chef創建自動…

AI在醫療領域的10大應用:從疾病預測到手術機器人

AI在醫療領域的10大應用&#xff1a;從疾病預測到手術機器人 系統化學習人工智能網站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目錄 AI在醫療領域的10大應用&#xff1a;從疾病預測到手術機器人摘要引言1. 醫學影像診斷&#xff1a;從靜態…

Win11 配置 Git 綁定 Github 賬號的方法與問題匯總

目錄 一、創建 Github 項目庫&#xff08;遠程倉庫&#xff09;二、配置安裝好的 Git1. 設置用戶信息2. 查看已配置的信息3. 建立本地倉庫4. Git 的常用命令1&#xff09;git checkout&#xff08;切換&#xff09;2&#xff09;git push&#xff08;上傳&#xff09;3&#xf…

6.應用層

6. 應用層 1. 概述 應用層是計算機網絡體系結構的最頂層&#xff0c;是設計和建立計算機網絡的最終目的&#xff0c;也是計算機網絡中發展最快的部分 早期基于文本的應用&#xff08;電子郵件、遠程登錄、文件傳輸、新聞組&#xff09;20世紀90年代將因特網帶入千家萬戶的萬維…

FPGA 100G UDP純邏輯協議棧

隨著器件等級的升高&#xff0c;高速serdes的線速率也隨之提高&#xff0c;RFSOC 4x最大可支持100G&#xff0c;主流方案為RDMA方案&#xff0c;該方案相對比較復雜&#xff0c;除了需要負責邏輯端的開發&#xff0c;還需操作系統中開發RDMA的驅動&#xff0c;對于對丟包不那么…

CSS實現DIV水平與垂直居中方法總結

大家好&#xff0c;歡迎來到程序視點&#xff01;我是你們的老朋友.小二&#xff01; CSS實現DIV水平與垂直居中方法總結 一、水平居中方案 標準方法 .center-div {margin-left: auto;margin-right: auto; }關鍵點&#xff1a;必須聲明DOCTYPE&#xff08;推薦XHTML 1.0 Tran…

Qt快速上手:QSettings高效配置讀寫實戰指南

文章目錄 前言一、QSettings初識&#xff1a;配置管理利器二、基礎操作三板斧2.1 文件讀寫基礎2.2 數據類型處理指南2.3 分組管理技巧 三、高級技巧&#xff1a;精準控制配置項3.1 監聽配置變更3.2 批量操作配置項 四、避坑指南&#xff1a;那些你可能會遇到的問題4.1 鍵順序重…