【在線五子棋對戰】二、websocket 服務器搭建

文章目錄

  • Ⅰ. WebSocket
    • 1、簡介
    • 2、特點
    • 3、原理解析
    • 4、報文格式
  • Ⅱ. WebSocketpp
    • 1、認識
    • 2、常用接口
    • 3、websocketpp庫搭建服務器
      • 搭建流程
      • 主體框架
      • 填充回調函數細節
    • 4、編寫 makefile 文件
    • 5、websocket客戶端

在這里插入圖片描述

Ⅰ. WebSocket

1、簡介

WebSocket 是從 HTML5 開始支持的一種網頁端和服務端保持長連接的消息推送機制。

  • 傳統的 web 程序都是屬于 “?問?答” 的形式,即客戶端給服務器發送了?個 HTTP 請求,服務器給客戶端返回?個 HTTP 響應。這種情況下服務器是屬于被動的一方,如果客戶端不主動發起請求服務器就無法主動給客戶端響應
  • 像網頁即時聊天或者我們做的五子棋游戲這樣的程序都是非常依賴 “消息推送” 的, 即需要服務器主動推動消息到客戶端。如果只是使用原生的 HTTP 協議,要想實現消息推送?般需要通過 “輪詢” 的?式實現,而輪詢的成本比較高并且也不能及時的獲取到消息的響應。

? 基于上述兩個問題, 就產生了 WebSocket 協議。WebSocket 更接近于 TCP 這種級別的通信?式,?旦連接建立完成客戶端或者服務器都可以主動的向對方發送數據。

? 并且要注意,WebSocket 和我們平時說的 Socket 是沒有半毛錢關系的,注意區分開來!

2、特點

  • 建立在 TCP 協議之上,支持雙向通信,實時性更強。
  • HTTP 協議有著良好的兼容性,握手階段采用 HTTP 協議,默認端口是 80443
  • 數據格式比較輕量,性能開銷小、通信高效。
  • 可以發送文本,也可以發送二進制數據。
  • 沒有同源限制,客戶端可以與任意服務器通信。
  • 協議標識符是 ws(如果加密,則為 wss),形式:ws://echo.websocket.org
  • 支持擴展。ws 協議定義了擴展,用戶可以擴展協議,或者實現自定義的子協議。(比如支持自定義壓縮算法等)

3、原理解析

? WebSocket 協議本質上是?個基于 TCP 的協議。為了建立?個 WebSocket 連接,客戶端瀏覽器首先要向服務器發起?個 HTTP 請求,這個請求和通常的 HTTP 請求不同,包含了?些附加頭信息,通過這個附加頭信息完成握手過程并升級協議的過程。

在這里插入圖片描述

? 通過一些抓包來分析一下切換過程中 HTTP 請求HTTP 響應的包有何不同:

? HTTP請求:

GET /ws hTTP/1.1								// URL通常會設置為/ws表示這是websocket
Host: localhost:2021							
Upgrade: websocket                              // 希望升級的協議格式
Connection: Upgrade								// 希望升級協議
Sec-Websocket-Key: mViTimINUhcF0fBHeX+wqA==		// 是客戶端發送的一個base64編碼的秘文,
Sec-Websocket-Version: 13						// 該websocket的版本
  • Sec-Websocket-Key 是客戶端發送的一個 base64 編碼的秘文,要求服務端返回一個對應加密的 Sec-Websocket-Accept 應答,否則客戶端會拋出 “Error during WebSocket handshake” 錯誤,并關閉連接。
  • Sec-Websocket-Version: 13 表示 websocket 的版本,如果服務端不支持該版本,需要返回一個 Sec-Websocket-Version 里面包含服務端支持的版本號。

? HTTP響應:

HTTP/1.1 101 Switching Protocols                       // 表示服務端接受websocket協議的
Connection: Upgrade									   // 升級協議
Upgrade: websocket									   // 升級的協議格式
Sec-Websocket-Accept: YLcYR/p/mS8hENqlgMXtFTggdv8=
  • Sec-Websocket-Accept 是服務端采用與客戶端一致的秘鑰計算出來后返回客戶端。
  • HTTP/1.1 101 Switching Protocols 表示服務端接受 WebSocket 協議的客戶端連接。

4、報文格式

在這里插入圖片描述

報文字段比較多,我們重點關注這幾個字段:

  • FIN
    • WebSocket 協議傳輸數據以消息為概念單位,?個消息有可能由?個或多個幀組成
    • 1 個比特大小:
      • 如果是 1,表示這是消息的最后一個分片
      • 如果是 0,表示這不是消息的最后一個分片
  • RSV1RSV2RSV3
    • 各占 1 個比特大小:
      • 保留字段,只在擴展時使用,若未啟用擴展則應置 1,若收到不全為 0 的數據幀,且未協商擴展則立即終止連接。
  • Opcode
    • 4 個比特,標志當前數據幀的類型:
      • 0x0:表示一個延續幀。當 Opcode0 時,表示本次數據傳輸采用了數據分片,當前收到的數據幀為其中一個數據分片。
      • 0x1:表示這是一個文本幀(frame)
      • 0x2:表示這是一個二進制幀(frame)
      • 0x3-0x7:保留的操作代碼,用于后續定義的非控制幀
      • 0x8:表示連接斷開
      • 0x9:表示這是一個 ping 操作
      • 0xA:表示這是一個 pong 操作
      • 0xB-0xF:保留的操作代碼,用于后續定義的控制幀
  • Mask
    • 表示是否要對數據載荷也就是 Payload數據 字段進行掩碼操作:
      • 從客戶端向服務端發送數據時,需要對數據進行掩碼操作。如果服務端接收到的數據沒有進行掩碼操作,服務端需要斷開連接!
      • 從服務端向客戶端發送數據時,不需要對數據進行掩碼操作。
    • 1 個比特大小:
      • 如果為 1,那么 Masking-key 字段中會定義一個掩碼鍵,并用這個掩碼鍵對數據載荷進行反掩碼。
  • Payload length
    • 數據載荷的長度,單位是字節。有可能為 7 位、7+16 位、7+64 位,假設 x 為數據載荷的長度,則其表示如下:
      • x 在[0,126) 內:數據的長度就是 Payload length 表示的大小
      • x 為 126:后續 2 個字節代表?個 16 位的無符號整數,該無符號整數的值為數據的長度
      • x 為 127:后續 8 個字節代表?個 64 位的無符號整數(最高位為 0),該無符號整數的值為數據的長度
  • Masking-key
    • 04 字節
      • Mask1,則包含 4 字節的 Masking-key
      • Mask0,則不包含 Masking-key
  • Payload data
    • 報文攜帶的載荷數據
      • 如果沒有協商使用擴展的話,擴展數據為 0 字節。所有的擴展都必須聲明擴展數據的長度,擴展如何使用必須在握手階段就協商好

Ⅱ. WebSocketpp

1、認識

? WebSocketpp 是?個跨平臺的開源(BSD許可證)頭部 專用C++庫,它實現了 RFC6455(WebSocket協議)和 RFC7692(WebSocketCompression Extensions)。它允許將 WebSocket 客戶端和服務器功能集成到 C++ 程序中。在最常見的配置中,全功能網絡 I/OAsio 網絡庫提供。

WebSocketpp 的主要特性包括:

  • 事件驅動的接口
  • ?持 HTTP/HTTPS、WS/WSS、IPv6
  • 靈活的依賴管理,如 Boost 庫/ C++11標準庫
  • 可移植性:Posix/Windows、32/64bit、Intel/ARM
  • 線程安全

? WebSocketpp 同時支持 HTTPWebsocket 兩種網絡協議,比較適用于我們本次的項目, 所以我們選用該庫作為項目的依賴庫用來搭建 HTTPWebSocket 服務器。

? 下面是該項目的?些常用網站:

  • github
  • 官網
  • 用戶手冊

2、常用接口

這里做一下下面接口和類的大概介紹:

  • 下面的幾個指定事件的回調事件,如 set_open_handler() 函數,它們都是用來 設置 針對不同事件而設置的處理函數,處理函數是由我們自己來寫的,因為 WebSocketpp 負責搭建服務器,它給不同的事件分配了不同的處理函數指針,比如 open_handler 其實就是握手成功后處理函數的指針,當服務器收到了指定的數據,觸發了指定的事件之后,就會通過函數指針去調用我們自己寫的那些處理函數!
  • server 類是繼承于 endpoint 類的,我們后面也是通過創建 server 類來完成搭建服務器的操作。
  • connection 這個類也很重要,它提供了一些接口,用于處理連接的事件和狀態,而 connection_hdl 是一個指向 connection 對象的輕量級句柄。它的作用是在 WebSocket 庫內部,用于管理和操作 connection 對象,以及在回調函數中傳遞連接對象的引用。
    • 一般來說我們通過 server 類的 get_con_from_hdl() 函數,通過 connection_hdl 就能獲得 connection_ptr 從而來操作 connection 類的函數!
  • 通常如 connectionmessage_buffer 等類都是用于在回調函數中請求和響應的類
namespace websocketpp 
{typedef lib::weak_ptr<void> connection_hdl;template <typename config>class endpoint : public config::socket_type {typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;typedef typename connection_type::ptr connection_ptr;typedef typename connection_type::message_ptr message_ptr;typedef lib::function<void(connection_hdl)> open_handler;typedef lib::function<void(connection_hdl)> close_handler;typedef lib::function<void(connection_hdl)> http_handler;typedef lib::function<void(connection_hdl, message_ptr)> message_handler;// websocketpp::log::alevel::none 禁止打印所有日志 void set_access_channels(log::level channels);   // 設置?志打印等級void clear_access_channels(log::level channels); // 清除指定等級的?志// 設置指定事件的回調函數void set_open_handler(open_handler h);       // websocket握?成功回調處理函數void set_close_handler(close_handler h);     // websocket連接關閉回調處理函數void set_message_handler(message_handler h); // websocket消息回調處理函數void set_http_handler(http_handler h);       // http請求回調處理函數// 發送數據接?void send(connection_hdl hdl, std::string& payload, frame::opcode::value op);void send(connection_hdl hdl, void* payload, size_t len, frame::opcode::value op);// 關閉連接接?void close(connection_hdl hdl, close::status::value code, std::string& reason);// 獲取connection_hdl 對應連接的connection_ptrconnection_ptr get_con_from_hdl(connection_hdl hdl);// websocketpp基于asio框架實現,init_asio?于初始化asio框架中的io_service調度器void init_asio();// 設置是否啟用地址重?void set_reuse_addr(bool value);// 設置endpoint的綁定監聽端?void listen(uint16_t port);// 對io_service對象的run接?封裝,?于啟動服務器std::size_t run();// websocketpp提供的定時器,以毫秒為單位timer_ptr set_timer(long duration, timer_handler callback);// 取消定時器,立刻觸發之前設置的callback函數std::size_t cancel();};template <typename config>class server: public endpoint<connection<config>, config> {// 初始化并啟動服務端監聽連接的accept事件處理void start_accept();}template <typename config>class connection: public config::transport_type::transport_con_type, public config::connection_base{// 發送數據接?error_code send(std::string& payload, frame::opcode::value op=frame::opcode::text);// 獲取http請求頭部中key關鍵字的std::string const& get_request_header(std::string const& key)// 獲取請求正文std::string const& get_request_body();// 設置響應狀態碼void set_status(http::status_code::value code);// 設置http響應正文void set_body(std::string const& value);// 添加http響應頭部字段void append_header(std::string const& key, std::string const& val);// 獲取http請求對象request_type const& get_request();// 獲取connection_ptr對應的connection_hdl connection_hdl get_handle();};namespace http {namespace parser {class parser {std::string const& get_header(std::string const& key)};class request : public parser {// 獲取請求方法std::string const& get_method()// 獲取請求uri接口std::string const& get_uri()};}}namespace message_buffer {// 獲取websocket請求中的payload數據類型frame::opcode::value get_opcode();// 獲取websocket中payload數據std::string const& get_payload();}namespace log {// 日志等級struct alevel {static level const none = 0x0;static level const connect = 0x1;static level const disconnect = 0x2;static level const control = 0x4;static level const frame_header = 0x8;static level const frame_payload = 0x10;static level const message_header = 0x20;static level const message_payload = 0x40;static level const endpoint = 0x80;static level const debug_handshake = 0x100;static level const debug_close = 0x200;static level const devel = 0x400;static level const app = 0x800;static level const http = 0x1000;static level const fail = 0x2000;static level const access_core = 0x00003003;static level const all = 0xffffffff;};}namespace http {// 狀態碼namespace status_code {enum value {uninitialized = 0,continue_code = 100,switching_protocols = 101,ok = 200,created = 201,accepted = 202,non_authoritative_information = 203,no_content = 204,reset_content = 205,partial_content = 206,multiple_choices = 300,moved_permanently = 301,found = 302,see_other = 303,not_modified = 304,use_proxy = 305,temporary_redirect = 307,bad_request = 400,unauthorized = 401,payment_required = 402,forbidden = 403,not_found = 404,method_not_allowed = 405,not_acceptable = 406,proxy_authentication_required = 407,request_timeout = 408,conflict = 409,gone = 410,length_required = 411,precondition_failed = 412,request_entity_too_large = 413,request_uri_too_long = 414,unsupported_media_type = 415,request_range_not_satisfiable = 416,expectation_failed = 417,im_a_teapot = 418,upgrade_required = 426,precondition_required = 428,too_many_requests = 429,request_header_fields_too_large = 431,internal_server_error = 500,not_implemented = 501,bad_gateway = 502,service_unavailable = 503,gateway_timeout = 504,http_version_not_supported = 505,not_extended = 510,network_authentication_required = 511};}}namespace frame {namespace opcode {enum value {continuation = 0x0,text = 0x1,binary = 0x2,rsv3 = 0x3,rsv4 = 0x4,rsv5 = 0x5,rsv6 = 0x6,rsv7 = 0x7,close = 0x8,ping = 0x9,pong = 0xA,control_rsvb = 0xB,control_rsvc = 0xC,control_rsvd = 0xD,control_rsve = 0xE,control_rsvf = 0xF,};}}
}

3、websocketpp庫搭建服務器

搭建流程

一般我們搭建服務器都是統一形式的,大概流程如下:

  1. 實例化 server 對象
    • 實例化的時候,因為命名空間一般我們不展開,所以要引用命名空間 websocketpp 里面的 server,注意不要落了模板,一般模板里面的 config 這里都是使用 websocketpp::config::asio
  2. 設置日志等級
  3. 初始化 asio 調度器和啟用地址重用
  4. 設置回調函數
    • 設置回調函數的時候,connection_hdl 類型是要通過命名空間 websocketpp 來引入的,就是 websocketpp::connection_hdl。而對于 set_http_handler() 函數來說,它還多了一個函數指針 message_ptr,因為它是屬于 endpoint 類,或者它的子類 server 的,所以通過我們 typedefserver 類型 wsserver_t 來取出這個函數指針,就是 wsserver_t::message_ptr
  5. 設置監聽窗口
  6. 獲取新連接
  7. 啟動服務器

主體框架

? 我們先將主體框架搭起來,其中頭文件我們要使用的是 server.hppasio_no_tls.hpp,因為 websocketpp 庫是放在 /usr/include/ 中的,所以要使用這些頭文件的話還得進入它們對應的路徑下面引入。

#include <iostream>
#include <string>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>using wsserver_t = websocketpp::server<websocketpp::config::asio>;void open_callback(websocketpp::connection_hdl hdl)
{}
void close_callback(websocketpp::connection_hdl hdl)
{}
void http_callback(websocketpp::connection_hdl hdl)
{}
void message_callback(websocketpp::connection_hdl hdl, wsserver_t::message_ptr msgptr)
{}
int main()
{// 1.實例化server對象wsserver_t server;// 2.設置日志等級server.set_access_channels(websocketpp::log::alevel::none);// 3.初始化asio調度器和地址重用server.init_asio();server.set_reuse_addr(true);// 4.設置回調函數server.set_open_handler(open_callback);server.set_close_handler(close_callback);server.set_message_handler(message_callback);server.set_http_handler(http_callback);// 5.設置監聽窗口server.listen(8080);// 6.開始獲取新連接server.start_accept();// 7.啟動服務器server.run();return 0;
}

? 但是一般我們在回調函數中還需要用到 server 對象的 指針 或者 引用,所以要多傳一個參數,但是我們不希望影響到這些函數的參數列表,因為比如這里的 typedef lib::function<void(connection_hdl)> open_handler 等函數已經要求參數中只能有一個參數,那要是我們想多傳一個參數又不想影響包裝器,怎么辦呢???

? 此時就需要使用 std::bind 綁定器,終于有用武之地了哈哈!

? 綁定器的作用就是可以將一些參數直接綁定到形參,相當于讓計算機幫我們自動填我們預先設置好的參數,就仿佛這個參數不存在一樣,有點像缺省參數,但不同的是,綁定器綁定的函數可以生成一個新的函數指針進行返回!

? 下面我們就給每個回調函數多傳一個 server 對象的指針,然后在設置回調函數那里進行參數綁定:

void open_callback(wsserver_t* server, websocketpp::connection_hdl hdl)
{}
void close_callback(wsserver_t* server, websocketpp::connection_hdl hdl)
{}
void http_callback(wsserver_t* server, websocketpp::connection_hdl hdl)
{}
void message_callback(wsserver_t* server, websocketpp::connection_hdl hdl, wsserver_t::message_ptr msgptr)
{}
int main()
{......// 4.設置回調函數server.set_open_handler(std::bind(open_callback, &server, std::placeholders::_1));server.set_close_handler(std::bind(close_callback, &server, std::placeholders::_1));server.set_message_handler(std::bind(message_callback, &server, std::placeholders::_1, std::placeholders::_2));server.set_http_handler(std::bind(http_callback, &server, std::placeholders::_1));	......
}

填充回調函數細節

  • http_callback() 函數

    // 任務:打印請求內容,并且設置響應內容
    void http_callback(wsserver_t* server, websocketpp::connection_hdl hdl)
    {// 首先將一些請求內容打印到終端wsserver_t::connection_ptr conn_ptr = server->get_con_from_hdl(hdl); // 獲取connection_ptrstd::cout << "body: " << conn_ptr->get_request_body() << std::endl;  // 打印請求正文websocketpp::http::parser::request req = conn_ptr->get_request();    // 獲取http請求對象std::cout << "method: " << req.get_method() << std::endl;            // 打印請求方法std::cout << "uri: " << req.get_uri() << std::endl;                  // 打印請求路徑資源std::cout << "version: " << req.get_version() << std::endl;          // 打印請求版本// 然后填充響應資源std::string body = "<html><body><h1>Hello Liren!</h1></body></html>"; // 寫一個簡單的html頁面格式conn_ptr->set_body(body);                                             // 設置響應正文conn_ptr->append_header("Content-Type", "text/html");                 // 設置響應頭部conn_ptr->set_status(websocketpp::http::status_code::ok);             // 設置響應狀態碼
    }
    
  • open_callback 函數

    void open_callback(wsserver_t* server, websocketpp::connection_hdl hdl)
    {std::cout << "websocket握手成功!" << std::endl;
    }
    
  • close_callback 函數

    void close_callback(wsserver_t* server, websocketpp::connection_hdl hdl)
    {std::cout << "websocket斷開連接!" << std::endl;
    }
    
  • message_callback 函數

    // 任務:收到一個消息進行打印,然后進行響應
    void message_callback(wsserver_t* server, websocketpp::connection_hdl hdl, wsserver_t::message_ptr msgptr)
    {// 打印獲取的消息wsserver_t::connection_ptr conn_ptr = server->get_con_from_hdl(hdl); // 獲取connection_ptrstd::cout << "message: " << msgptr->get_payload() << std::endl;      // 打印消息// 響應std::string callback = "server say: client say " + msgptr->get_payload(); // 創建響應內容conn_ptr->send(callback, websocketpp::frame::opcode::text);               // 發送響應
    }
    

4、編寫 makefile 文件

? 因為 websocketpp 用到了 pthreadboost_system,所以要在編譯的時候指定:

server:wsserver.cppg++ -o $@ $^ -std=c++11 -lboost_system -lpthread.PHONY:clean
clean:rm -f server

5、websocket客戶端

? 如果使用的是 http 客戶端,也就是直接用瀏覽器輸入 ip + 端口號,那么就能直接訪問了。其連接等時候 觸發的是 http_callback() 函數

? 而這里我們用自己寫一個 html 頁面來充當 websocket 客戶端,來測試一下服務器是否能正確的收發信息,其 觸發的是 message_callback() 函數

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Test Websocket</title></head><body><input type="text" id="message"><button id="submit">提交</button><script>// 創建 websocket 實例// ws://192.168.51.100:8888// 類比http:// ws表示websocket協議// 192.168.51.100 表示服務器地址// 8888表?服務器綁定的端?let websocket = new WebSocket("ws://81.71.97.127:8080/");// 處理連接打開的回調函數websocket.onopen = function() {alert("連接建?");}// 處理收到消息的回調函數// 控制臺打印消息websocket.onmessage = function(e) {alert("收到消息: " + e.data);}// 處理連接異常的回調函數websocket.onerror = function() {alert("連接異常");}// 處理連接關閉的回調函數websocket.onclose = function() {alert("連接關閉");}// 實現點擊按鈕后, 通過 websocket實例 向服務器發送請求let input = document.querySelector('#message');let button = document.querySelector('#submit');button.onclick = function() {console.log("發送消息: " + input.value);websocket.send(input.value);}</script></body>
</html>

? 然后我們在桌面可以創建一個 html 文件,將其復制進去之后,打開這個文件,就能測試了!

在這里插入圖片描述

在這里插入圖片描述

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

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

相關文章

針對異構數據的聯邦學習

在聯邦學習中&#xff0c;數據異構性是指不同客戶端之間的數據分布差異&#xff0c;包括數據的特征空間、標簽空間以及數據量等方面的差異。處理異構數據是聯邦學習中的一個重要挑戰&#xff0c;因為異構數據可能導致模型訓練過程中的性能不穩定、收斂速度較慢&#xff0c;甚至…

【判斷自整除數】2022-4-6

緣由是判斷自整除數的&#xff0c;這個我的結果是正確的&#xff0c;但是提交就有運行錯誤是怎么回事啊-編程語言-CSDN問答 void 自整除數字() {//所謂的自整除數字就是該數字可以整除其每一個位上的數字。 //對一個整數n,如果其各個位數的數字相加得到的數m能整除n,則稱n為自…

@Import原理與實戰

文章目錄 前言一、導入普通類二、導入ImportSelector實現類三、導入ImportBeanDefinitionRegistrar實現類四、Import注解的解析4.1、解析實現ImportSelector的候選bean4.2、解析實現ImportBeanDefinitionRegistrar的候選bean4.3、DeferredImportSelector的特殊處理 總結 前言 I…

day 18進行聚類,進而推斷出每個簇的實際含義

浙大疏錦行 對聚類的結果根據具體的特征進行解釋&#xff0c;進而推斷出每個簇的實際含義 兩種思路&#xff1a; 你最開始聚類的時候&#xff0c;就選擇了你想最后用來確定簇含義的特征&#xff0c; 最開始用全部特征來聚類&#xff0c;把其余特征作為 x&#xff0c;聚類得到…

Java并發編程實戰 Day 11:并發設計模式

【Java并發編程實戰 Day 11】并發設計模式 開篇 這是"Java并發編程實戰"系列的第11天&#xff0c;今天我們聚焦于并發設計模式。并發設計模式是解決多線程環境下常見問題的經典解決方案&#xff0c;它們不僅提供了優雅的設計思路&#xff0c;還能顯著提升系統的性能…

iview組件庫:當后臺返回到的數據與使用官網組件指定的字段不匹配時,進行修改某個屬性名再將response數據渲染到頁面上的處理

1、需求導入 當存在前端需要的數據的字段渲染到表格或者是一些公共的表格組件展示數據時的某個字段名與后臺返回的字段不一致時&#xff0c;那么需要前端進行稍加處理&#xff0c;而不能直接this.list res.data;這樣數據是渲染不出來的。 2、后臺返回的數據類型 Datalist(pn) …

Ubuntu下有關UDP網絡通信的指令

1、查看防火墻狀態&#xff1a; sudo ufw status # Ubuntu 2、 檢查系統全局廣播設置 # 查看是否忽略廣播包&#xff08;0表示接收&#xff0c;1表示忽略&#xff09; sysctl net.ipv4.icmp_echo_ignore_broadcasts# 查看是否允許廣播轉發&#xff08;1表示允許&#xff09…

vue3:十六、個人中心-修改密碼

一、頁面效果 頁面展示當前用戶名(只讀),展示需要輸入的當前密碼,輸入新的密碼以及確認密碼的提交表單 二、初始建立 1、建立密碼修改頁面 在個人中心文件夾中寫入新頁面UpdatepwdView.vue 2、新建路由 在路由頁面中寫入修改密碼頁面 3、新建菜單 在菜單布局菜單頁面中寫…

GitFlow 工作模式(詳解)

今天再學項目的過程中遇到使用gitflow模式管理代碼&#xff0c;因此進行學習并且發布關于gitflow的一些思考 Git與GitFlow模式 我們在寫代碼的時候通常會進行網上保存&#xff0c;無論是github還是gittee&#xff0c;都是一種基于git去保存代碼的形式&#xff0c;這樣保存代碼…

【Vue3】(三)vue3中的pinia狀態管理、組件通信

目錄 一、vue3的pinia 二、【props】傳參 三、【自定義事件】傳參 四、【mitt】傳參 五、【v-model】傳參&#xff08;平常基本不寫&#xff09; 六、【$attrs】傳參 七、【$refs和$parent】傳參 八、provide和inject 一、vue3的pinia 1、什么是pinia&#xff1f; pinia …

【DAY43】復習日

內容來自浙大疏錦行python打卡訓練營 浙大疏錦行 作業&#xff1a; kaggle找到一個圖像數據集&#xff0c;用cnn網絡進行訓練并且用grad-cam做可視化 進階&#xff1a;并拆分成多個文件

xtp+ctp 交易系統接口簡介

CTP&#xff08;上海期貨交易所綜合交易平臺&#xff09;和 XTP&#xff08;中泰證券極速交易平臺&#xff09;是中國金融市場中兩個重要的證券期貨交易系統&#xff0c;它們在定位、架構和應用場景上有顯著區別&#xff1a; 1. 開發主體與服務領域 維度CTPXTP開發公司上海期貨…

阿里云Alibaba Cloud安裝Docker與Docker compose【圖文教程】

個人記錄 進入控制臺&#xff0c;找到定時與自動化任務 進入‘安裝/卸載擴展程序’ 點擊‘安裝擴展程序’ 選擇docker社區版&#xff0c;點擊下一步與確定&#xff0c;等待一會 安裝成功 查詢版本 查詢docker sudo docker version查詢docker compose sudo docker compo…

非Root用戶啟動SSH服務經驗小結

各位看官&#xff0c;小子我先問個問題&#xff1a;是不是經常在容器里想開個SSH&#xff0c;卻發現自己不是root&#xff0c;處處碰壁&#xff1f;這是常態。多數容器鏡像精簡到連SSH服務都沒有&#xff0c;就算有&#xff0c;咱們普通用戶也沒權限啟動它。 今天小子就介紹一…

Windows開機自動啟動中間件

WinSW&#xff08;Windows Service Wrapper 是一個開源的 Windows 服務包裝器&#xff0c;它可以幫助你將應用程序打包成系統服務&#xff0c;并實現開機自啟動的功能。 一、下載 WinSW 下載 WinSW-x64.exe v2.12.0 (?? 更多版本下載) 和 sample-minimal.xml 二、配置 WinS…

【CATIA的二次開發23】抽象對象Document涉及文檔激活控制的方法

在CATIA VBA開發中,Document對象是最核心、最基礎的對象之一。它代表了當前在CATIA會話中打開的一個文檔(文件)。 幾乎所有與文件操作、模型訪問相關的操作都始于獲取一個Document對象。Document對象包含多種方法和屬性,以下介紹Document對象方法和屬性 一、Document對象方…

基于多維視角的大模型提升認知醫療過程層次激勵編程分析

系統架構設計 #mermaid-svg-k3W5lvie1sP3T956 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-k3W5lvie1sP3T956 .error-icon{fill:#552222;}#mermaid-svg-k3W5lvie1sP3T956 .error-text{fill:#552222;stroke:#55222…

【評測】Qwen3-Embedding模型初體驗

回到目錄 【評測】Qwen3-Embedding模型初體驗 模型的介紹頁面 0.6B運行配置&#xff1a;筆記本i5-8265U&#xff0c;16G內存&#xff0c;無GPU核顯運行&#xff0c;win10操作系統 8B運行配置&#xff1a;AMD8700G&#xff0c;64G內存&#xff0c;4090D 24G顯存&#xff0c;ub…

MPLAB X IDE ?軟件安裝與卸載

1、下載MPLAB X IDE V6.25 MPLAB X IDE | Microchip Technology 正常選Windows&#xff0c;點擊Download&#xff0c;等待自動下載完成&#xff1b; MPLAB X IDE 一臺電腦上可以安裝多個版本&#xff1b; 2、安裝MPLAB X IDE V6.25 右鍵以管理員運行&#xff1b;next; 勾選 I a…

PLC入門【2】PLC的接線

02 PLC的接線 PLC 的品牌介紹&#xff0c;PLC的接線 1、PLC 大體分為歐式和日式 2、只要學會三菱的&#xff0c;整個日式的也差不多會了。 3、PLC 分為晶體管輸出和繼電器輸出。 4、PLC 接線都差不多的 我們主要是講這個三菱的 PLC&#xff0c; 三菱和臺達的 PLC&#xff0c;…