Makefile 跟著走快點

来源:http://www.cnblogs.com/life2refuel/archive/2016/11/06/6024963.html
-Advertisement-
Play Games

Makefile linux 程式開發的一道坎. 也是linux開發中最重要的基本功之一, 不求深入, 但求讀懂. ...


引言  - 從"HelloWorld"開始

  Makefile 是Linux C 程式開發最重要的基本功. 代表著整個項目編譯和最終生成過程.本文重點是帶大家瞭解真實項目中那些簡易的Makefile規則構建.

本文參照資料

   GNU make   -  https://www.gnu.org/software/make/manual/make.html  

   跟我一起寫Makefile  - http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:%E6%A6%82%E8%BF%B0 

   入門基礎Makefile概述  - https://github.com/loverszhaokai/GNUMakeManual_CN

 

推薦需要簡單看看上面資料. 特別是第三個入門教程, 瞭解基礎make語法.  看完後那我們擴展之路開始了, 先hello world 講起. 素材 mian.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#define ALEN(arr) (sizeof(arr)/sizeof(*arr))

/*
 * 簡單的demo測試
 */
int main(int argc, char * argv[]) {
    int i;
    const char * strs[] = {
        "走著走著,就散了,回憶都淡了",
        "看著看著,就累了,星光也暗了;",
        "聽著聽著,就醒了,開始埋怨了;",
        "回頭髮現,你不見了,突然我亂了。",
    };
    
    srand((unsigned)time(NULL));
    for(;;) {
        /*
         *    \e[ 或 \033[ 是 CSI,用來操作屏幕的。
         *    \e[K 表示從游標當前位置起刪除到 EOL (行尾)
         *    \e[NX 表示將游標往X方向移動N,X = A(上) / B(下) / C(左) / D(右),\e[1A 就是把游標向上移動1行
         */
        printf("\033[1A\033[K"); //先回到上一行, 然後清除這一行  

        // 隨機輸出一段話
        i = rand()%ALEN(strs);
        puts(strs[i]);

        sleep(3);
    }    

    return 0;
}

編譯上面程式的第一個Makefile 文件內容如下

main.out:main.c
    gcc -g -Wall -o $@ $^

執行過程就是通過shell執行make, 我們簡單翻譯一下上面寫法的含義.

  目標 main.out  依賴 main.c ;  main.c 已經存在(因為是存在的文件) 那就執行規則 (gcc -g -Wall -o $@ $^).

  其中 $@ 表示所有目標, $^表示所有依賴. 

是不是很簡單.當然上面Makefile還存在一些潛規則.

  所有執行規則都是以\t開始; 第一個目標就是make過程唯一執行的起點;

 

再講之前我們再扯一點gcc 相關的積累知識. 否則寫Makefile都是無米之炊. 

# 中間插入一段關於gcc 的前戲
gcc –E –o main.i mian.c    # -E是預處理展開巨集,生成詳細c文件, -o是輸出
gcc –S –o main.s main.i    # -S 是編譯階段, 將c文件生成彙編語言
gcc –c –o main.o main.s    # -c 是彙編階段, 生成機器碼
gcc –o main.exe main.o     # 鏈接階段, -o 生成目標執行程式

gcc –g      # 編譯中加入調試信息, 方便gdb調試, 還有-ggdb3 支持巨集調試等
gcc –Wall    # 輸出所有警告信息
gcc –O2        # 開啟調優, O2等級調優

gcc –I(i大寫)            # 導入頭文件目錄,方便 *.h文件查找
gcc –L(l 大寫)          # 導入庫文件目錄,方便 *.so和*.a文件查找
gcc –l(l 小寫)           # 導入庫文件, 例如-lpthread, 相當於依次查找導入 libpthread.so/libpthread.a 文件
gcc –static –l(l 小寫)   # 指定只查找 靜態庫 lib*.a 文件, linux約定庫文件都是 lib開頭


ar rc libheoo.a hello.o world.o                    # 將*.o 文件打包成 libheoo.a 靜態庫
gcc –fPIC –shared –o libheoo.so hello.o world.o    # 將*.o 文件打包成 libheoo.so 動態庫

到這裡儲備方面的講完畢了.   --<-<-<@

 

前言  -  介紹一下實際例子中語法套路

   首先升級一下上面Makefile文件, 如下(如果你複製沒法執行, 請檢查規則開頭字元是\t)

# 構建全局編譯操作巨集
CC = gcc 
CFLAGS = -g -Wall -O2 
RUNE = $(CC) $(CFLAGS) -o $@ $^
RUNO = $(CC) -o $@ $<

# 構建偽命令
.PHONY:all clean cleanall

# 第一個標簽, 是make的開始
all:main.out

main.out:main.c
    $(RUNE)

# 清除操作
clean:
    -rm -rf *.i *.s *.o *~
cleanall:clean
   -rm -rf *.out *.out *.a *.so

我們先說一下Makefile中變數的使用, 就是上面 "="那種基礎語法說明.

 

關於Makefile 變數總結如下

關於上面變數的使用這裡做一個總結. 

a. = 聲明變數
加入存在下麵場景
…
CC = cc
…
CC = gcc

那麼make的時候, $(CC) 就是 gcc, 會全局替換. 
對於 = 聲明的可以認為是一個全局遞歸替換巨集. 

b. := 聲明變數

… 
srcdir := ./coroutine
tardir := ./Debug
… 
上面就是一般語言中普通變數. 

c. ?= 聲明變數

Foo ?= bar

上面意思是 $(foo)不存在, 那就將 bar 給它. 等同於
ifeq ($(origin FOO), undefined)
  FOO = bar
endif

d. += 聲明變數

objects = main.o foo.o bar.o utils.o
objects += another.o
等同於

objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o

趁著熱度舉個例子, 先不解惑. 

CC = cc
FOO := foo 
BAR ?= bar 
HEO := heo 

all :
    echo $(CC)
    echo $(FOO)
    echo $(BAR)
    echo $(HEO) 

HEO += world
FOO := FOO 
CC = gcc 

執行結果如下, 如下圖 . 通過Demo外加上下麵運行結果圖, 應該會有收穫.

通過上面我們可以發現 := 和 = 聲明的變數都是最終全局替換之後的結果. 他們二者細微差別, 我還是通過例子來說吧.

一切都在不言中, 那麼關於Makefile變數中語法講解完畢. 順帶說一些小細節吧,

  1). Makefile 中 一切從簡單開始, 能用 = 就不要用 :=

  2). 變數具備全部作用域 , 推薦全部用大寫命名

  3). 多查最開始我推薦的資料

 

接著變數往後講,繼續分析其它例子

上面 .PHONY 是 Makefile中偽命令. 預設套路寫法. 定義命令名稱, 可以通過 make 命令名稱調用.

其中 all 是Makefile第一個運行目標,  從它入口. clean , cleanall 偽命令 通過 make clean ; make cleanall 執行.

主要是清除生成的中間文件. 希望你能明白, 自己演示一下, 是不是這樣的.

這裡我們開始一個新的例子了. 具體參照

  C協程庫的編譯文件  https://github.com/wangzhione/scoroutine/blob/master/Makefile

# 全局替換變數
CC = gcc 
CFLAGS = -g -Wall -O2 
RUNE = $(CC) $(CFLAGS) -o $@ $^

# 聲明路徑變數
SRC_PATH := ./coroutine
TAR_PATH := ./Debug

# 構建偽命令
.PHONY:all clean cleanall

# 第一個標簽, 是make的開始
all:$(TAR_PATH)/main.out

$(TAR_PATH)/main.out:main.o scoroutine.o
    $(CC) $(CFLAGS) -o $@ $(addprefix $(TAR_PATH)/, $^ )

$(TAR_PATH):
    mkdir $@
    
%.o:$(SRC_PATH)/%.c | $(TAR_PATH)
    $(CC) $(CFLAGS) -c -o $(TAR_PATH)/$@ $<
    
# 清除操作
clean:
  -rm -rf $(TAR_PATH)/*.i $(TAR_PATH)/*.s $(TAR_PATH)/*.o $(TAR_PATH)/*~
cleanall:clean
  -rm -rf $(TAR_PATH)

從頭開始分析它的具體含義.

1) 開頭全局變數定義部分, 個人習慣問題其實也可以用 := . 最終得到 RUNE = gcc -g -Wall -O2 -o $@ $^ .

2) 路徑聲明部分, 用 := 聲明, 支持中間拼接. 用=也可以, 都是條條大路同羅馬, 自己多檢查一下. 以後我可能全部用 = 聲明全局遞歸的字面變數聲明. 

3) .PHONY 聲明瞭 3個偽命令. 不會立即執行的命令, 依賴 make 命令名稱 主動調用

4) all 依賴 於 $(TAR_PATH)/main.out 就是依賴於 ./coroutine/main.out. 剛好下麵存在

$(TAR_PATH)/main.out:main.o scoroutine.o
    $(CC) $(CFLAGS) -o $@ $(addprefix $(TAR_PATH)/, $^ ) 

這條規則. 其中又依賴於 main.o 和 scoroutine.o 目標. 那麼二者也會做新的目標, 就這樣遞歸的找下去.
後面找到了 %.o, Makefile中%是匹配符, 例如 main.o % 就相當於 main部分.
其中addprefix 是GNU make內置的函數的其中一個, 需要用到的時候多查文檔就行了.

為每一個可以分割的子單元上加上一個首碼, 這個首碼就是它的第一個參數.

5) 對於下麵這段很實用, 通配符 + | 生成必要文件的語法

%.o:$(SRC_PATH)/%.c | $(TAR_PATH)
    $(CC) $(CFLAGS) -c -o $(TAR_PATH)/$@ $<

以上是一個通用匹配規則, %.o 目標依賴於 ..../%.c 具體文件. 後面 | 跟的也是一個依賴目標. 這個目標只會在第一次不存在的時候才會被構建.

更加詳細的說明可以參照第一個參照資料 "4.3 Types of Prerequisites" 部分.  這個語法用的很多, 用於構建一次生成所需的目錄信息.

6) 最後就是剩餘clean, cleanall偽命令. 定義清除中間文件等.

是不是想罵die, 但是上面那些都自行搗鼓了一遍, 基本就越過Makefile初級部分, 能夠寫出能看的編譯文件O(∩_∩)O哈哈~

 

正文  - 來個小框架Makefile試試水

  先找一個特別老的, 很水的一個Makefile 試試. 具體參照

  一個控制台小項目編譯文件  https://github.com/wangzhione/sconsole_project/blob/master/linux_sc_template/Makefile

C = gcc
DEBUG = -g -Wall -D_DEBUG
#指定pthread線程庫
LIB = -lpthread -lm
#指定一些目錄
DIR = -I./module/schead/include -I./module/struct/include
#具體運行函數
RUN = $(CC) $(DEBUG) -o $@ $^ $(LIB) $(DIR)
RUNO = $(CC) $(DEBUG) -c -o $@ $^ $(DIR)

# 主要生成的產品
all:test_cjson_write.out test_csjon.out test_csv.out test_json_read.out test_log.out\
 test_scconf.out test_tstring.out

#挨個生產的產品
test_cjson_write.out:test_cjson_write.o schead.o sclog.o tstring.o cjson.o
    $(RUN)
test_csjon.out:test_csjon.o schead.o sclog.o tstring.o cjson.o
    $(RUN)
test_csv.out:test_csv.o schead.o sclog.o sccsv.o tstring.o
    $(RUN)
test_json_read.out:test_json_read.o schead.o sclog.o sccsv.o tstring.o cjson.o
    $(RUN)
test_log.out:test_log.o schead.o sclog.o
    $(RUN)
test_scconf.out:test_scconf.o schead.o scconf.o tree.o tstring.o sclog.o
    $(RUN)
test_tstring.out:test_tstring.o tstring.o sclog.o schead.o
    $(RUN)

#產品主要的待鏈接文件
test_cjson_write.o:./main/test_cjson_write.c
    $(RUNO)
test_csjon.o:./main/test_csjon.c
    $(RUNO)
test_csv.o:./main/test_csv.c
    $(RUNO)
test_json_read.o:./main/test_json_read.c
    $(RUNO)
test_log.o:./main/test_log.c 
    $(RUNO) -std=c99
test_scconf.o:./main/test_scconf.c
    $(RUNO)
test_tstring.o:./main/test_tstring.c
    $(RUNO)

#工具集機械碼,待別人鏈接
schead.o:./module/schead/schead.c
    $(RUNO)
sclog.o:./module/schead/sclog.c
    $(RUNO)
sccsv.o:./module/schead/sccsv.c
    $(RUNO)
tstring.o:./module/struct/tstring.c
    $(RUNO)
cjson.o:./module/schead/cjson.c
    $(RUNO)
scconf.o:./module/schead/scconf.c
    $(RUNO)
tree.o:./module/struct/tree.c
    $(RUNO)

#刪除命令
clean:
    rm -rf *.i *.s *.o *.out __* log ; ls -hl
.PHONY:clean

上面那些註釋已經表達了一切了吧, 確實好水. 但是特別適合練手, 每一個生成目標都有規則對應. 費力但是最直接. 實在沒有沒有好講的, 扯一點

1) GNU make 指定的編譯文件是 makefile 或 Makefile. 推薦用Makefile, 是一個傳統吧. 因為C項目都是小寫, 用大寫開頭以作區分.

2) Makefile 中 同樣以 \ 來起到一整行的效果

3) 其它目標, 依賴, 規則.只要存在那麼Makefile就可以自動推導. 當然它依賴文件創建時間戳, 只有它變化了Makefile才會重新生成目標.

Makefile點心結束了. 以上就是make使用本質, 生成什麼, 需要什麼, 執行什麼. 推薦練練手, 手冷寫不了代碼.

 

最後來點水果

  simplec c的簡易級別框架  https://github.com/wangzhione/simplec/blob/master/Makefile

##################################################################################################
#                            0.前期編譯輔助參數支持                                                 #
##################################################################################################
SRC_PATH         ?= ./simplec
MAIN_DIR         ?= main
SCHEAD_DIR       ?= module/schead
SERVICE_DIR      ?= module/service
STRUCT_DIR       ?= module/struct
TEST_DIR         ?= test
TAR_PATH         ?= ./Output
BUILD_DIR        ?= obj

# 指定一些目錄
DIR     =    -I$(SRC_PATH)/$(SCHEAD_DIR)/include -I$(SRC_PATH)/$(SERVICE_DIR)/include \
            -I$(SRC_PATH)/$(STRUCT_DIR)/include

# 全局替換變數
CC        = gcc 
LIB       = -lpthread -lm
CFLAGS    = -g -Wall -O2 -std=gnu99

# 運行指令信息
define NOW_RUNO
$(notdir $(basename $(1))).o : $(1) | $$(TAR_PATH)
    $$(CC) $$(CFLAGS) $$(DIR) -c -o $$(TAR_PATH)/$$(BUILD_DIR)/$$@ $$<
endef

# 單元測試使用, 生成指定主函數的運行程式
RUN_TEST = $(CC) $(CFLAGS) $(DIR) --entry=$(basename $@) -nostartfiles -o \
    $(TAR_PATH)/$(TEST_DIR)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))

# 產生具體的單元測試程式
define TEST_RUN
$(1) : $$(notdir $$(basename $(1))).o libschead.a $(2) | $$(TAR_PATH)
    $$(RUN_TEST) $(LIB)
endef
    
##################################################################################################
#                            1.具體的產品生產                                                      #
##################################################################################################
.PHONY:all clean cleanall

all : main.out\
    $(foreach v, $(wildcard $(SRC_PATH)/$(TEST_DIR)/*.c), $(notdir $(basename $(v))).out)

# 主運行程式main
main.out:main.o simplec.o libschead.a libstruct.a test_sctimeutil.o
    $(CC) $(CFLAGS) $(DIR) -o $(TAR_PATH)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v)) $(LIB)

# !!!!! - 生成具體的單元測試程式 - 依賴個人維護 - !!!!!
$(eval $(call TEST_RUN, test_array.out, array.o))
$(eval $(call TEST_RUN, test_atom_rwlock.out))
$(eval $(call TEST_RUN, test_cjson.out, tstr.o))
$(eval $(call TEST_RUN, test_cjson_write.out, tstr.o))
$(eval $(call TEST_RUN, test_csv.out, tstr.o))
$(eval $(call TEST_RUN, test_json_read.out, tstr.o))
$(eval $(call TEST_RUN, test_log.out))
$(eval $(call TEST_RUN, test_scconf.out, tstr.o tree.o))
$(eval $(call TEST_RUN, test_scoroutine.out, scoroutine.o))
$(eval $(call TEST_RUN, test_scpthread.out, scpthread.o scalloc.o))
$(eval $(call TEST_RUN, test_sctimer.out, sctimer.o scalloc.o))
$(eval $(call TEST_RUN, test_sctimeutil.out))
$(eval $(call TEST_RUN, test_tstring.out, tstr.o))
$(eval $(call TEST_RUN, test_xlsmtojson.out, tstr.o))

##################################################################################################
#                            2.先產生所需要的所有機器碼文件                                          #
##################################################################################################

# 迴圈產生 - 所有 - 鏈接文件 *.o
SRC_CS = $(wildcard\
    $(SRC_PATH)/$(MAIN_DIR)/*.c\
    $(SRC_PATH)/$(TEST_DIR)/*.c\
    $(SRC_PATH)/$(SCHEAD_DIR)/*.c\
    $(SRC_PATH)/$(SERVICE_DIR)/*.c\
    $(SRC_PATH)/$(STRUCT_DIR)/*.c\
)
$(foreach v, $(SRC_CS), $(eval $(call NOW_RUNO, $(v))))

# 生產 -相關- 靜態庫
libschead.a : $(foreach v, $(wildcard $(SRC_PATH)/$(SCHEAD_DIR)/*.c), $(notdir $(basename $(v))).o)
    ar cr $(TAR_PATH)/$(BUILD_DIR)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))
libstruct.a : $(foreach v, $(wildcard $(SRC_PATH)/$(STRUCT_DIR)/*.c), $(notdir $(basename $(v))).o)
    ar cr $(TAR_PATH)/$(BUILD_DIR)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))

##################################################################################################
#                            3.程式的收尾工作,清除,目錄構建                                          #
##################################################################################################
$(TAR_PATH):
    -mkdir -p $@/$(BUILD_DIR)
    -mkdir -p $@/test/config
    -cp -r $(SRC_PATH)/test/config $@/test

# 清除操作
clean :
    -rm -rf $(TAR_PATH)/$(BUILD_DIR)/*

cleanall :
    -rm -rf $(TAR_PATH)

 

具體可以參照simplec 項目查看, 我們抽一部分重點講解

define NOW_RUNO
$(notdir $(basename $(1))).o : $(1) | $$(TAR_PATH)
    $$(CC) $$(CFLAGS) $$(DIR) -c -o $$(TAR_PATH)/$$(BUILD_DIR)/$$@ $$<
endef

上面定義了一個語句塊 NOW_RUNO. 其中語句塊中除了要接收的參數可以用$(1), $(2) ..., 其它都是兩個$$開頭, 否則就被替換了. 使用方法就是

$(eval $(call NOW_RUNO, $(v)))

通過$eval(), $(call ) 這種套路調用. call NOW_RUNO, 後面添加都是 NOW_RUNO語句塊的函數了.

這裡說一個Makefile處理的潛在小問題, 當你傳入參數是依賴項時候, 如果不是直接通過唯一一個參數傳入進去,

那麼解析的是當成多個依賴項處理.所以上面只有 $(1)做依賴項.

Makefile中 foreach語法也很好用等同於shell語法傳參方式.

$(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))
將第二個$^通過空格分隔成單個的v代替, 被替換為第三個中一部分. $(foreach ...)執行完畢最終返回一個拼接好的串

 

在簡單補充幾個函數說明 例如

$(1) => $$(notdir $$(basename $(1))).o <=> ./simplec/main/main.c => main.o

其中 nodir函數得到文件名, basename函數得到文件名不包過.和.後面部分.
wildcard 函數是得到指定匹配規則下的文件全路徑拼接.
最後面 -rm 那些, 加了首碼 - 是為了當Makefile執行到這如果運行出錯, 不停止繼續前行.
通過上面Makefile最終跑起來後, 會生成一個Output目錄, 再在內部生成 obj, test, ...
還是很有學習價值的. 有興趣的可以試試.
希望通過上面講解, 能夠使你以後閱讀其它更高級項目的編譯文件不那麼生疏. (* ̄(エ) ̄)

 

後記  -  突然想起了什麼, 笑了笑 我自己 ...

  伽羅  -  http://music.163.com/#/artist/desc?id=21309

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 同事反饋他在一測試伺服器(CentOS Linux release 7.2.1511)上修改了/etc/profile文件後,使用source命令不能生效,讓我幫忙看看,結果使用SecureCRT一登錄就發現下麵錯誤信息: Last login: Fri Nov 4 00:42:45 2016 fr... ...
  • 虛擬桌面快捷鍵: 新建虛擬桌面 Control+Win+D 切換虛擬桌面 Control+Win+左/右方向鍵 關閉虛擬桌面 Control+Win+F4 顯示虛擬桌面列表 Win+Tab ...
  • 在系統維護的過程中,隨時可能有需要查看 CPU 使用率,並根據相應信息分析系統狀況的需要。在 CentOS 中,可以通過 top 命令來查看 CPU 使用狀況。運行 top 命令後,CPU 使用狀態會以全屏的方式顯示,並且會處在對話的模式 -- 用基於 top 的命令,可以控制顯示方式等等。退出 t ...
  • 在linux中,常常都要提示設置: umask 022 其作用如下: 功能說明:指定在建立文件時預設的許可權掩碼。語 法:umask [-S][許可權掩碼]補充說明:umask可用來設定[許可權掩碼]。[許可權掩碼]是由3個八進位的數字所組成,將現有的存取許可權減掉許可權掩碼後,即可產生建立文件時預設的許可權。參 ...
  • Linux 掛載 備註:如果掛載到有文件的目錄下 會遮擋原先文件 必須卸載掛載的硬碟 才能顯示 Linux umount 卸載 ...
  • avahi-daemon是一種Linux操作系統上運行在客戶機上實施查找基於網路的Zeroconf service的服務守護進程。 該服務可以為Zeroconf網路實現DNS服務發現及DNS組播規範。 用戶程式通過Linux D-Bus信息傳遞接收發現到網路服務和資源的通知。 該守護進程配合緩存用戶 ...
  • 從沒想到自己會開通博客,之前在編程上遇到問題 總會來到博客園來檢索點文檔看一下。 今天終於動手註冊開通了一個博客 希望能在這記錄下自己在程式世界的 一點一滴 每一步腳印 每一次進步 望大家共勉 ...
  • **************************************************************************************** ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...