TCP 套接字--服務器相關

1.創建 TCP 套接字

int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);

函數原型:

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

domain協議族(地址族)AF_INET(IPv4)
type套接字類型SOCK_STREAM(TCP)
protocol協議類型0(自動選擇)
(1)?domain(協議族)
  • AF_INET:IPv4 地址族(最常用)。
  • AF_INET6:IPv6 地址族。
  • AF_UNIX(或?AF_LOCAL):本地進程間通信(UNIX 域套接字)。
(2)?type(套接字類型)
  • SOCK_STREAM
    • 面向連接的?TCP 套接字
    • 提供可靠、雙向、基于字節流的通信。
  • SOCK_DGRAM
    • 無連接的?UDP 套接字
    • 提供不可靠、無邊界的數據報服務。
  • SOCK_RAW
    • 原始套接字,用于自定義協議(如 ICMP)。
(3)?protocol(協議類型)
  • 0
    • 讓系統自動選擇與?domain?和?type?匹配的協議。
    • 對于?AF_INET + SOCK_STREAM,默認選擇?TCP
  • 其他常見值:
    • IPPROTO_TCP(顯式指定 TCP,但通常用?0?即可)。
    • IPPROTO_UDP(用于?SOCK_DGRAM)。

?

返回值

  • 成功:返回一個?非負整數,即?套接字描述符server_sockfd)。
  • 失敗:返回?-1,并設置?errno(如?EMFILEENFILEEACCES?等)。

2.設置套接字選項?SO_REUSEADDR?的標準用法,用于控制套接字的行為

setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))?

  • 作用
    • 允許套接字(sockfd)綁定到?處于?TIME_WAIT?狀態的地址(例如服務器重啟時)。
    • 避免?bind()?失敗(EADDRINUSE?錯誤)。
  • 參數說明
    • sockfd:目標套接字描述符。
    • SOL_SOCKET:表示操作套接字層選項(通用選項)。
    • SO_REUSEADDR:選項名稱,允許地址重用。
    • &reuse:指向選項值的指針(int?類型,1?啟用,0?禁用)。
    • sizeof(reuse):選項值的大小。

3.TCP 服務器綁定(bind())操作?

struct sockaddr_in server_sockaddr;server_sockaddr.sin_family = AF_INET; // IPv4 協議族server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 綁定到所有本地接口(0.0.0.0) server_sockaddr.sin_port = htons(voice_SysParameter.port); // 綁定到指定端口(網絡字節序)// 綁定套接字 
if (bind(server_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr)) == -1) 
{perror("bind"); // 輸出錯誤信息 goto voice_tcp_Thread_TaskProcError; // 錯誤處理}
  • 關鍵點
    • AF_INET:使用 IPv4 協議。
    • INADDR_ANY:綁定到所有本地網絡接口(即?0.0.0.0),允許通過任何本地 IP 訪問服務。
    • htons()?和?htonl():將主機字節序(小端或大端)轉換為網絡字節序(大端)。
    • bind()?返回值:成功返回?0,失敗返回?-1?并設置?errno

4.TCP 連接超時檢測與清理??

for (int i = 0; i < voice_MaxConnectNum; i++) {// 檢查條件:// 1. ConnfdCurTime[i] > 0(記錄過心跳時間)// 2. 當前時間 - 最后一次心跳時間 > 允許的心跳超時時間(voice_SysParameter.heartBeat)// 3. Connfd[i] > 0(連接有效)if ((ConnfdCurTime[i] > 0) && (ConnfdCurTime[i] + voice_SysParameter.heartBeat < HeartBeatCnt) && (Connfd[i] > 0)) {struct sockaddr_in sa = {0};int len = sizeof(sa);// 獲取客戶端的 IP 地址和端口信息getpeername(Connfd[i], (struct sockaddr *)&sa, &len);// 關閉超時連接,并打印日志(連接描述符 + 客戶端 IP)close(Connfd[i]);printf("Connfd Tcp__closed=%d %s\n", Connfd[i], inet_ntoa(sa.sin_addr));// 重置連接狀態Connfd[i] = 0;ConnfdCurTime[i] = 0;}
}

5.檢查所有已記錄的子設備(SubDev)狀態

for (int j = 0; j < voice_SysParameter.connectNo; j++) {// 如果子設備的 IP 地址為空,跳過處理if (strcmp(voice_SysParameter.SubDevState.SubDevState[j].ip, "") == 0) continue;int i = 0;// 遍歷所有 TCP 連接,檢查是否有連接匹配當前子設備的 IPfor (i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] == 0) continue; // 跳過無效連接(Connfd[i]=0 表示空閑)struct sockaddr_in sa = {0};int len = sizeof(sa);// 獲取當前連接的客戶端 IP 地址getpeername(Connfd[i], (struct sockaddr *)&sa, &len);// 如果當前連接的 IP 匹配子設備的 IP,跳出循環if (strcmp(voice_SysParameter.SubDevState.SubDevState[j].ip, inet_ntoa(sa.sin_addr)) == 0) break;}// 如果遍歷完所有連接都沒找到匹配的 IP(i == voice_MaxConnectNum)if (i == voice_MaxConnectNum) {// 清除子設備狀態(標記為未連接)voice_SysParameter.SubDevState.SubDevState[j].slaveNo = (char)-1;strcpy(voice_SysParameter.SubDevState.SubDevState[j].ip, "");voice_SysParameter.SubDevState.SubDevState[j].status = 0;}
}

?6.使用?select()?監聽多個文件描述符(file descriptors)的可讀事件

// 清空文件描述符集合
FD_ZERO(&rset);
FD_SET(server_sockfd, &rset);  // 將服務器套接字加入監聽集合
maxfd = server_sockfd;         // 初始化最大文件描述符為服務器套接字// 遍歷所有客戶端連接,更新監聽集合和最大文件描述符
for (int i = 0; i < voice_MaxConnectNum; i++) {maxfd = maxfd > Connfd[i] ? maxfd : Connfd[i]; // 更新最大文件描述符if (Connfd[i] > 0)                            // 只監聽有效的連接FD_SET(Connfd[i], &rset);                 // 加入監聽集合
}// 調用 select() 監聽 I/O 事件
nready = select(maxfd + 1, &rset, NULL, NULL, &timeout);// 超時處理
if (nready == 0) {continue; // select 超時,繼續循環
}
  • 核心功能:使用?select()?監聽服務器套接字和客戶端連接的可讀事件,實現 I/O 多路復用。
  • 關鍵點
    • FD_ZERO()?+?FD_SET()?初始化監聽集合。
    • select(maxfd + 1, &rset, ...)?阻塞等待 I/O 事件。
    • FD_ISSET()?檢查哪個文件描述符就緒。
  • 改進方向
    • 改用?epoll()/kqueue()?提高性能。
    • 增加錯誤處理和連接管理。
    • 動態調整超時時間。

7.處理?select()?返回的就緒文件描述符?

for (int i = 0; i < voice_MaxConnectNum; i++) {if (FD_ISSET(Connfd[i], &rset)) {  // 檢查 Connfd[i] 是否就緒(可讀)ConnfdCurTime[i] = HeartBeatCnt; // 更新心跳時間戳if (voice_ConTask(Connfd[i]) == -1) { // 處理客戶端數據,返回 -1 表示錯誤struct sockaddr_in sa = {0};int len = sizeof(sa);// 1. 獲取客戶端 IP 地址(用于日志或調試)getpeername(Connfd[i], (struct sockaddr *)&sa, &len);// 2. 關閉連接并清理資源close(Connfd[i]);printf("Connfd Tcp__closed=%d %s\n", Connfd[i], inet_ntoa(sa.sin_addr));Connfd[i] = 0;          // 標記連接為無效ConnfdCurTime[i] = 0;   // 清空心跳時間戳}}
}

8.處理單個客戶端連接的數據接收、解析和響應

int voice_ConTask(int sockfd) {char buffer[2048];          // 接收數據的緩沖區memset(buffer, 0, sizeof(buffer)); // 清空緩沖區// 1. 接收客戶端數據int len = recv(sockfd, buffer, sizeof(buffer), 0);// 2. 檢查是否收到 "exit" 命令(客戶端主動關閉)if (strcmp(buffer, "exit") == 0) {printf("sockfd: %d exited.1\n", sockfd);return -1; // 返回 -1 表示連接需要關閉}// 3. 檢查連接是否已關閉(len=0 表示客戶端斷開)else if (len == 0) {printf("sockfd: %d exited.2\n", sockfd);return -1; // 返回 -1 表示連接需要關閉}// 4. 處理有效數據else {// 調用 voice_TaskCommand 處理數據,并返回響應長度len = voice_TaskCommand(buffer, len);if (len > 0) {// 發送響應數據給客戶端send(sockfd, buffer, len, 0);}}return 1; // 返回 1 表示處理成功,繼續保持連接
}

9.TCP 服務器?處理?新客戶端連接?的邏輯

// 1. 檢查服務器 socket 是否可讀(即是否有新連接到達)
if (FD_ISSET(server_sockfd, &rset)) {struct sockaddr_in client_addr;socklen_t length = sizeof(client_addr);// 2. 接受新連接int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);if (conn < 0) {perror("New client connect Error");continue; // 接受失敗,跳過本次循環} else {printf("new client accepted.\n");}// 3. 將新連接存入連接池(Connfd 數組)for (int i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] == 0) { // 找到空閑位置Connfd[i] = conn; // 存儲新連接的文件描述符printf("Connfd[%d]=%d\n\r", i, conn);printf("IP:%s Connected...\n\r", inet_ntoa(client_addr.sin_addr));break; // 存入后跳出循環}// 4. 處理連接數超限if (i == voice_MaxConnectNum - 1) { // 遍歷完所有位置printf("New Conn Num limit to %d \n\r", voice_MaxConnectNum);close(conn); // 關閉新連接(因為無法存儲)}}
}

完整代碼

/*** @brief TCP 服務器線程處理函數* @param p 線程參數(結構體指針,包含線程啟動標志)* @return void* 線程返回值(未使用)*/
void* voice_tcp_Thread_TaskProc(void* p) {// 1. 解析線程參數struct voice_Thread_PARA_S *pstPara = (struct voice_Thread_PARA_S*)p;// 2. 創建服務器 socket(IPv4 + TCP)int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd < 0) {perror("socket creation failed");goto voice_tcp_Thread_TaskProcError;}// 3. 初始化連接池和心跳時間記錄數組int Connfd[voice_MaxConnectNum] = {0};          // 存儲客戶端連接的文件描述符int ConnfdCurTime[voice_MaxConnectNum] = {0};    // 記錄最后一次心跳時間(秒)// 4. 設置 SO_REUSEADDR 選項(避免端口占用)int reuse = 1;if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {perror("Setting SO_REUSEADDR failed");goto voice_tcp_Thread_TaskProcError;}// 5. 綁定服務器地址和端口struct sockaddr_in server_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));server_sockaddr.sin_family = AF_INET;server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 監聽所有網卡server_sockaddr.sin_port = htons(voice_SysParameter.port); // 轉換端口為網絡字節序if (bind(server_sockfd, (struct sockaddr*)&server_sockaddr, sizeof(server_sockaddr)) == -1) {perror("bind failed");goto voice_tcp_Thread_TaskProcError;}printf("bind success.\n");// 6. 開始監聽(待處理連接隊列長度為 20)if (listen(server_sockfd, 20) == -1) {perror("listen failed");goto voice_tcp_Thread_TaskProcError;}printf("listen success.\n");printf("Tcp Server: %s:%d\n", voice_SysParameter.ip, voice_SysParameter.port);// 7. 主循環(處理客戶端連接和數據)while (pstPara->bThreadStart == 1) {// 7.1 獲取當前時間(用于心跳檢測)struct timeval tv;gettimeofday(&tv, NULL);int current_time = tv.tv_sec;// 7.2 心跳檢測:關閉超時未活動的連接for (int i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] > 0 && ConnfdCurTime[i] > 0 && (current_time - ConnfdCurTime[i] > voice_SysParameter.heartBeat)) {struct sockaddr_in client_addr;socklen_t len = sizeof(client_addr);getpeername(Connfd[i], (struct sockaddr*)&client_addr, &len); // 獲取客戶端 IPprintf("Heartbeat timeout, close connection: fd=%d, IP=%s\n", Connfd[i], inet_ntoa(client_addr.sin_addr));close(Connfd[i]); // 關閉連接Connfd[i] = 0;    // 清空連接池位置ConnfdCurTime[i] = 0;}}// 7.3 檢查設備狀態(如果客戶端斷開,更新設備狀態)for (int j = 0; j < voice_SysParameter.connectNo; j++) {if (strcmp(voice_SysParameter.SubDevState.SubDevState[j].ip, "") == 0) continue;int i;for (i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] <= 0) continue; // 跳過無效連接struct sockaddr_in client_addr;socklen_t len = sizeof(client_addr);getpeername(Connfd[i], (struct sockaddr*)&client_addr, &len);if (strcmp(voice_SysParameter.SubDevState.SubDevState[j].ip, inet_ntoa(client_addr.sin_addr)) == 0) {break; // 找到匹配的連接}}// 如果未找到匹配連接,清空設備狀態if (i == voice_MaxConnectNum) {voice_SysParameter.SubDevState.SubDevState[j].slaveNo = -1;strcpy(voice_SysParameter.SubDevState.SubDevState[j].ip, "");voice_SysParameter.SubDevState.SubDevState[j].status = 0;}}// 7.4 使用 select 監聽 I/O 事件fd_set rset;FD_ZERO(&rset);FD_SET(server_sockfd, &rset); // 監聽服務器 socket(接受新連接)int maxfd = server_sockfd;for (int i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] > 0) {FD_SET(Connfd[i], &rset); // 監聽所有客戶端連接maxfd = (Connfd[i] > maxfd) ? Connfd[i] : maxfd; // 更新最大文件描述符}}// 設置 select 超時時間(1 秒)struct timeval timeout;timeout.tv_sec = 1;timeout.tv_usec = 0;int nready = select(maxfd + 1, &rset, NULL, NULL, &timeout);if (nready <= 0) continue; // 超時或錯誤,繼續循環// 7.5 處理客戶端數據for (int i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] > 0 && FD_ISSET(Connfd[i], &rset)) {ConnfdCurTime[i] = current_time; // 更新心跳時間// 調用業務邏輯處理函數(如解析協議、處理請求)if (voice_ConTask(Connfd[i]) == -1) {struct sockaddr_in client_addr;socklen_t len = sizeof(client_addr);getpeername(Connfd[i], (struct sockaddr*)&client_addr, &len);printf("Client disconnected or error, close connection: fd=%d, IP=%s\n", Connfd[i], inet_ntoa(client_addr.sin_addr));close(Connfd[i]);Connfd[i] = 0;ConnfdCurTime[i] = 0;}}}// 7.6 處理新連接if (FD_ISSET(server_sockfd, &rset)) {struct sockaddr_in client_addr;socklen_t length = sizeof(client_addr);int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);if (conn < 0) {perror("accept failed");continue;}printf("New client accepted: fd=%d, IP=%s\n", conn, inet_ntoa(client_addr.sin_addr));// 將新連接存入連接池for (int i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] == 0) {Connfd[i] = conn;ConnfdCurTime[i] = current_time; // 初始化心跳時間printf("Connfd[%d]=%d, IP=%s Connected\n", i, conn, inet_ntoa(client_addr.sin_addr));break;}// 連接數超限if (i == voice_MaxConnectNum - 1) {printf("Connection limit reached (%d), reject new client\n", voice_MaxConnectNum);close(conn); // 關閉新連接}}}}// 8. 清理資源(線程退出時)for (int i = 0; i < voice_MaxConnectNum; i++) {if (Connfd[i] > 0) {close(Connfd[i]);Connfd[i] = 0;}}close(server_sockfd);return NULL;voice_tcp_Thread_TaskProcError:if (server_sockfd >= 0) close(server_sockfd);return NULL;
}

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

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

相關文章

六、搭建springCloudAlibaba2021.1版本分布式微服務-admin監控中心

前言Spring Boot Actuator 是 spring-boot 自帶監控功能 &#xff0c;可以幫助實現對程序內部運行情況監控&#xff0c;比如監控狀況、Bean 加載情況、環境變量、日志信息、線程信息等。 Spring Boot Admin是一個針對 spring-boot 的 actuator 接口進行 UI 美化封裝的監控工具。…

輕量級遠程開發利器:Code Server與cpolar協同實現安全云端編碼

前言&#xff1a;作為一款專為Web環境設計的VS Code托管方案&#xff0c;Code Server通過精簡架構重新定義了遠程開發體驗。其核心優勢在于將完整的編輯器功能封裝于輕量容器中——僅需不到200MB內存即可運行基礎服務&#xff0c;并支持在樹莓派等低性能設備上流暢操作。系統采…

圖論:最小生成樹

今天要介紹兩中最小生成樹的算法&#xff0c;分別是prim算法和kruskal算法。 最小生成樹是所有節點的最小連通子圖&#xff0c;即&#xff1a;以最小的成本&#xff08;邊的權值&#xff09;將圖中所有節點鏈接到一起。 圖中有n個節點&#xff0c;那么一定可以用n-1條邊將所有節…

haproxy七層代理

1、負載均衡Load Balance(LB) 概念 負載均衡&#xff1a;是一種服務或基于硬件設備等實現的高可用反向代理技術&#xff0c;負載均衡將特定的業務(web服務、網絡流量等)分擔給指定的一個或多個后端特定的服務器或設備&#xff0c;從而提高了 公司業務的并發處理能力、保證了業務…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - 微博文章數據可視化分析-點贊區間實現

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程&#xff0c;持續更新中&#xff0c;計劃月底更新完&#xff0c;感謝支持。今天講解微博文章數據可視化分析-點贊區間實現 視頻…

Redis實戰(3)-- 高級數據結構zset

有序集合&#xff08;ZSET&#xff09;&#xff1a;可以用作相關有序集合相對于哈希、列表、集合來說會有一點點陌生,但既然叫有序集合,那么它和集合必然有著聯系,它保留了集合不能有重復成員的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下標作為排序依據…

Mistral AI開源 Magistral-Small-2507

宣布Magistral——Mistral AI推出的首款推理模型&#xff0c;專精于垂直領域、具備透明化特性與多語言推理能力。 最優秀的人類思維并非線性——它穿梭于邏輯、洞見、不確定性與發現之間。推理型語言模型讓我們得以將復雜思考和深度理解交由AI增強或代勞&#xff0c;提升了人類…

【Kotlin】如何實現靜態方法?(單例類、伴生對象、@JvmStatic)

靜態方法 靜態方法&#xff08;類方法&#xff09;&#xff1a;不需要創建實例就可以調用&#xff08;直接通過類名調用&#xff09;的方法 Java 中的靜態方法&#xff08;static&#xff09; public class Util {public static void doAction() {//...} }調用&#xff1a;Util…

SQL Schema 和Pandas Schema什么意思

在數據處理和分析領域&#xff0c;SQL Schema 和 Pandas Schema 分別指的是在不同數據處理環境中數據的結構定義&#xff0c;以下為你詳細介紹&#xff1a;SQL Schema含義SQL Schema&#xff08;模式&#xff09;是數據庫對象的一個邏輯容器&#xff0c;它定義了數據庫中表、視…

機器學習(一)KNN,K近鄰算法(K-Nearest Neighbors)

&#x1f4a1; 建議初學者掌握KNN作為理解其他復雜算法&#xff08;如SVM、決策樹、神經網絡&#xff09;的基石。K近鄰算法&#xff08;K-Nearest Neighbors, KNN&#xff09;詳解&#xff1a;原理、實踐與優化K近鄰算法&#xff08;K-Nearest NeighboKrs&#xff0c;簡稱KNN&…

Qt 多線程數據庫操作優化

在多線程應用中&#xff0c;數據庫操作往往是性能瓶頸與穩定性風險的高發區。當多個線程同時讀寫數據庫時&#xff0c;若處理不當&#xff0c;輕則出現查詢卡頓、事務沖突&#xff0c;重則導致數據錯亂、連接泄漏甚至程序崩潰。Qt作為跨平臺框架&#xff0c;提供了QSql系列類支…

Qt 狀態機框架:復雜交互邏輯的處理

Qt狀態機框架&#xff08;Qt State Machine Framework&#xff09;是一個強大的工具&#xff0c;用于簡化復雜的交互邏輯和狀態管理。它基于UML狀態圖概念&#xff0c;提供了聲明式的方式來定義對象行為&#xff0c;特別適合處理具有多種狀態和轉換的場景&#xff08;如GUI交互…

【docker】DM8達夢數據庫的docker-compose以及一些啟動踩坑

摘要&#xff1a;本文介紹了通過docker-compose配置啟動達夢數據庫(DM8)的方法。配置包括容器鏡像、端口映射、數據卷掛載以及關鍵環境變量設置&#xff08;如字符集、大小寫敏感等&#xff09;。也說明了啟動過程可能遇到的一些問題。通過docker-compose啟動達夢數據庫可以按照…

服務器中的防火墻設置需要打開嗎

服務器中的防火墻屬于是一種保護計算機網絡不會受到未經授權的網絡設備所訪問的技術手段&#xff0c;防火墻還有著將內部網絡和外部網絡進行隔離的功能&#xff0c;在網絡之間創建安全屏障&#xff0c;以此來保護網絡中數據的安全。防火墻是一種網絡安全系統&#xff0c;可以幫…

Java項目:基于SSM框架實現的社區團購管理系統【ssm+B/S架構+源碼+數據庫+畢業論文+答辯PPT+遠程部署】

摘 要 使用舊方法對社區團購信息進行系統化管理已經不再讓人們信賴了&#xff0c;把現在的網絡信息技術運用在社區團購信息的管理上面可以解決許多信息管理上面的難題&#xff0c;比如處理數據時間很長&#xff0c;數據存在錯誤不能及時糾正等問題。 這次開發的社區團購系統有…

介紹一下static關鍵字

在Java中&#xff0c;被static修飾的成員稱為靜態成員&#xff0c;static關鍵字可以用來修飾方法或者成員變量&#xff0c;且被static修飾的方法或者成員變量屬于類方法或者類屬性&#xff0c;也就是說被static修飾的方法或者成員變量不是單獨存儲在某一個對象的空間&#xff0…

【Java學習|黑馬筆記|Day23】網絡編程、反射、動態代理

【DAY23】 文章目錄【DAY23】一.網絡編程1&#xff09;三要素1.1&#xff09;IPInetAddress類的使用1.2&#xff09;端口號1.3&#xff09;協議2.1&#xff09;UDP協議發送數據2.2&#xff09;UDP協議接收數據2.3&#xff09;UDP的三種通信方式3.1&#xff09;TCP協議的發送和接…

【Zephyr】Window下的Zephyr編譯和使用

工具下載 參考文檔&#xff08;Getting Started Guide — Zephyr Project Documentation&#xff09;中介紹&#xff0c;可以直接通過winget下載&#xff1a; winget download Kitware.CMake Ninja-build.Ninja oss-winget.gperf python Git.Git oss-winget.dtc wget 7zip.7z…

圖論(BFS)構造鄰接表(運用隊列實現搜索)

碼蹄集OJ-夏日漫步 #include<bits/stdc.h> using namespace std; int n; int a[200010],dis[200010],qaq[1000010]; vector<int>son[200010]; int que[200010]; int main( ) {memset(qaq,-1,sizeof(qaq));memset(dis,-1,sizeof(dis));cin>>n;for(int i1;i…

vue怎么實現導入excel表功能

<el-uploadref"upload":action"aaa":on-change"importProject"name"excelFile"multiple:auto-upload"false":show-file-list"false"><el-button type"warning">導入</el-button><…