HarmonyOS USB DDK助你輕鬆實現USB驅動開發

来源:https://www.cnblogs.com/HarmonyOSDev/archive/2022/03/28/16068757.html
-Advertisement-
Play Games

本期,我們將為大家帶來HDF驅動框架中USB DDK的解析與指導。 ...


HDF(Hardware Driver Foundation)驅動框架是HarmonyOS硬體生態開放的基礎,為開發者提供了驅動載入、驅動服務管理和驅動消息機制等驅動能力,讓開發者能精準且高效地開發驅動程式。

 

本期,我們將為大家帶來HDF驅動框架中USB DDK的解析與指導。

 

一、USB DDK介紹


USB(Universal Serial Bus)通用串列匯流排,用於規範電腦與外部設備的連接和通訊,包含了主機端(Host)和設備端(Device)。其中,主機端負責USB匯流排中的數據傳輸及埠管理,設備端則可以連接各種外設,所以USB驅動開發又分為主機端驅動開發和設備端驅動開發。 

 

由於基於內核態開發的USB驅動功能擴展性較差,目前開發者通常選擇Libusb庫進行USB驅動開發。該庫是一種跨平臺的用戶態開源USB通信庫,可以滿足開發者基於用戶態開發功能驅動的需求。但是,由於Libusb庫是完全按照USB協議來封裝介面的,所以需要開發者對USB協議要有較深的瞭解才能很好的使用,對開發者的要求相對較高,讓很多比較初級的開發者望而卻步。為了讓更多的開發者都能進行基於用戶態的USB驅動開發,HDF引入了USB DDK開發套件。 

 

USB DDK(USB DriverDevelop Kit)是HDF驅動框架為開發者提供的USB驅動程式開發套件,包括USB Host DDK及USB Device DDK兩部分,支持基於用戶態開發USB設備驅動的同時,還提供了豐富的USB驅動開發能力,讓廣大開發者能精準且高效的開發USB驅動程式。

 

下麵,我們將一一道來。  

 

1)USB Host DDK


USB Host DDK給開發者提供了主機端USB驅動開發能力,按照功能分類三大類,分別是DDK初始化類、interface對象操作類及request對象操作類。併為開發者提供了普通模式和專家模式兩種開發模式。普通模式下,開發者可通過USBDDK API直接完成相關USB數據讀寫操作,不需要過多關註底層傳輸細節。

 

專家模式下,開發者通過USB RAW API直接訪問OS平臺USB通道的介面,自定義實現更加複雜的功能。目的是給驅動層留有更靈活,更強大的擴展方案,同時也能夠相容現有驅動,便於移植。USBHost DDK架構如圖1所示:

 

圖1 USB Host DDK架構

 

(1)USB Interface Pool負責USBInterface管理。提供USB Interface申請和回收,USB Interface記錄設備埠信息以及資源。USB Interface Pool按照USB Port對USB Interface進行分類管理。同時,此模塊還提供了USB DDK API,方便開發者USB數據讀寫操作。 

 

(2)USB Protocol Layer提供USB協議封裝,根據USB協議對設備IO/控制命令的“翻譯/解析”,同時負責設備描述符的管理,根據USB Device上報的枚舉信息,匹配對應的描述符,並構建對應的USB Interface,並加入到USB Interface Pool中管理。 

 

(3)Device IO Manager負責USBIO請求管理,提供了同步IO和非同步IO管理機制,對於非同步IO,IO Manager負責將該請求記錄下來,然後通過Raw API Library提供的介面依次處理待發送的IO請求;當收到USB控制器應答的處理結果後,IO接收線程負責解析並上報處理結果給上層調用者。 

 

(4)Raw API Library抽象了底層OS能力,定義了統一的OS能力介面,對外提供了USB RAW API,讓開發者自定義實現更加複雜的驅動功能。 

 

(5)OS Adapter用於封裝與平臺(Linux和LiteOS)相關的操作,根據不同平臺配置編譯對應平臺的封裝介面。在Linux平臺上,訪問USBFS的操作,全部都封裝在這個模塊中;而在LiteOS平臺上,基於FreeBSD USB框架的設備訪問操作,對應的也都全部封裝在這個模塊中。 

 

(6)PNP Notify用於動態監測USB狀態變化,當有新設備添加/移除時,變化設備信息。同時將所有USB設備信息都通過KHDF上報給UHDF側的PNPNotify Manager模塊來完成載入/卸載第三方功能驅動。 

 

2)USB Device DDK


USB Device DDK給開發者提供了設備端USB驅動開發能力。例如,USB埠動態註冊和去註冊能力,開發者可以基於能力實現USB埠的動態添加和組合;動態實例化能力,支持根據動態下發設備、配置、介面及端點描述符創建設備實例及傳輸通道;用戶態的數據發送及接收能力,支持用戶態下發送及接收數據;複合設備能力,支持一個物理設備上多個邏輯設備,實現多個邏輯設備間隔離,並支持不同邏輯設備同時被不同的應用進程訪問。

 

USB Device DDK架構如圖2所示: 

 

圖2 USB Device DDK架構

 

(1)SDK IF負責將USB設備按照設備、介面、管道進行邏輯劃分,對配置管理、設備管理、IO管理進行封裝。此模塊還向開發者提供了設備創建、獲取介面、接收Event事件、收發數據等設備測驅動開發的能力介面。

 

(2)Configuration Manager負責解析HCS文件描述的USB描述符信息,得到的USB描述符信息用於設備創建,同時模塊還提供了自定義屬性的讀取、創建、刪除、修改等操作。

 

(3)Device Manager負責根據配置模塊解析的USB描述符,並根據USB描述符創建設備。同時模塊還負責獲取設備、刪除設備、獲取設備狀態,獲取設備上面介面信息。 

 

(4)IO Manager負責數據的讀寫,包括Events事件、數據讀寫完成事件的接受,支持同步和非同步模式數據讀寫。 

 

(5)Adapter IF主要是對複合設備配置驅動及通用功能驅動設備節點操作進行封裝,為上層提供統一的設備管理介面。 

 

(6)Adapter該模塊由複合設備配置驅動及通用功能驅動提供。  

 

二、USB DDK開髮指導


相信大家已對USB DDK已經有了一定的認識。下麵,我們來看看如何使用USB DDK來開發USB Host和USB Device驅動程式吧。 

 

1)USB Host的開發


USB Host(主機端驅動)主要完成協議封裝、設備管理、驅動安裝與卸載等。通過上文的介紹,開發者可通過USB DDK API和USB RAW API來實現主機端驅動。 

 

1. USB DDK API的使用

 

USB DDK API主要實現主機端USB數據讀寫操作,如圖3所示,是USB DDK API提供的部分介面。                                              

 

圖3 USB DDK API部分介面

 

使用步驟如下:

 

(1) 配置驅動匹配表,完成主機端驅動總體信息的配置,具體如下:

 

struct UsbPnpMatchIdTable {
//驅動模塊名,該欄位的值必須和驅動入口結構的moduleName一致
const char *moduleName;
//驅動對外發佈服務的名稱,必須唯一
const char *serviceName;
//驅動私有數據匹配關鍵字
const char *deviceMatchAttr;
//從該欄位開始(包含該欄位)之後數據長度,以byte為單位
uint8_t length;
//USB驅動匹配規則
uint16_t matchFlag;
//廠商編號
uint16_t vendorId;
//產品編號
uint16_t productId;
//設備出廠編號,低16位
uint16_t bcdDeviceLow;
//設備出廠編號,高16位
uint16_t bcdDeviceHigh;  
//USB分配的設備類代碼
uint8_t deviceClass;
//USB分配的子類代碼
uint8_t deviceSubClass;
//USB分配的設備協議代碼
uint8_t deviceProtocol;
//介面類型,根據實際需要可填寫多個
uint8_t interfaceClass[USB_PNP_INFO_MAX_INTERFACES];
//介面子類型,根據實際需要可填寫多個
uint8_t interfaceSubClass[USB_PNP_INFO_MAX_INTERFACES];
//介面所遵循的協議,根據實際需要可填寫多個
uint8_t interfaceProtocol[USB_PNP_INFO_MAX_INTERFACES];
//介面的編號,根據實際需要可填寫多個
uint8_t interfaceNumber[USB_PNP_INFO_MAX_INTERFACES];
};

 

其中matchFlag表示驅動匹配規則,每個bit表示一種匹配方式,其取值如下: 

 

enum {
    USB_PNP_NOTIFY_MATCH_VENDOR = 0x0001,
    USB_PNP_NOTIFY_MATCH_PRODUCT = 0x0002,
    USB_PNP_NOTIFY_MATCH_DEV_LOW = 0x0004,
    USB_PNP_NOTIFY_MATCH_DEV_HIGH = 0x0008,
    USB_PNP_NOTIFY_MATCH_DEV_CLASS = 0x0010,
    USB_PNP_NOTIFY_MATCH_DEV_SUBCLASS = 0x0020,
    USB_PNP_NOTIFY_MATCH_DEV_PROTOCOL = 0x0040,
    USB_PNP_NOTIFY_MATCH_INT_CLASS = 0x0080,
    USB_PNP_NOTIFY_MATCH_INT_SUBCLASS = 0x0100,
    USB_PNP_NOTIFY_MATCH_INT_PROTOCOL = 0x0200,
    USB_PNP_NOTIFY_MATCH_INT_NUMBER = 0x0400,
};


(2) USB主機端驅動開發工具包初始化,使用如下介面: 

 

int32_t UsbInitHostSdk(struct UsbSession **session)


(3) 待步驟2初始化完後獲取UsbInterface對象,使用如下介面: 

 

const struct UsbInterface *UsbClaimInterface(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr, uint8_t interfaceIndex);


(4) 打開步驟3獲取到的UsbInterface介面對象,獲取對應介面的UsbInterfaceHandle對象,使用如下介面: 

 

UsbInterfaceHandle *UsbOpenInterface(const struct UsbInterface *interfaceObj);


(5) 根據步驟4獲取到的UsbInterfaceHandle對象,獲取指定索引為pinpeIndex的pipeInfo信息,使用如下介面: 

 

int32_t UsbGetPipeInfo(const UsbInterfaceHandle *interfaceHandle, uint8_t settingIndex, uint8_t pipeId, struct UsbPipeInfo *pipeInfo);


(6) 為步驟4獲取到的UsbInterfaceHandle預先分配待發送的IO Request對象,使用如下介面: 

 

struct UsbRequest *UsbAllocRequest(const UsbInterfaceHandle *interfaceHandle, int isoPackets, int length);


(7) 根據輸入參數params填充步驟6預先分配的IO Request,使用如下介面: 

 

int32_t UsbFillRequest(const struct UsbRequest *request, const UsbInterfaceHandle *interfaceHandle, const struct UsbRequestParams *params);


(8) 提交IO Request對象,可以選擇同步或非同步兩種模式,使用如下介面: 

 

int32_t UsbSubmitRequestSync(const struct UsbRequest *request);//發送同步IO請求
int32_t UsbSubmitRequestAsync(const struct UsbRequest *request);//發送非同步IO請求


2. USB RAW API 的使用

 

USB RAW API主要實現USB更加複雜的功能,如獲取描述符信息、獲取設備指針、複位設備、提交傳輸請求等,如圖4所示,是USB RAW API提供的部分介面。 

 

圖4 USB RAW API

 

使用步驟如下:

 

(1) 同USB DDK API的步驟1一樣,需先進行驅動匹配表配置。 

 

(2) 初始化Host RAW,使用如下介面: 

 

int32_t UsbRawInit(struct UsbSession **session);


(3) 待步驟2完成後打開USB設備,使用如下介面: 

 

UsbRawHandle *UsbRawOpenDevice(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr);


(4) 待步驟3完成後獲取描述符,通過描述符獲取介面、端點信息,使用如下介面: 

 

int32_t UsbRawGetConfigDescriptor(const UsbRawDevice *rawDev, uint8_t configIndex, struct UsbRawConfigDescriptor **config);


(5) 分配Request,並根據不同的傳輸類型使用相應的介面對Request進行填充: 

 

int32_t UsbRawFillBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於批量傳輸的請求
int32_t UsbRawFillControlSetup(const unsigned char *setup, const struct UsbControlRequestData *requestData);
int32_t UsbRawFillControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於控制傳輸的請求
int32_t UsbRawFillInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於中斷傳輸的請求
int32_t UsbRawFillIsoRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於同步傳輸的請求


(6) 提交IO Request對象,可以選擇同步或非同步兩種模式,分別使用如下介面: 

 

int32_t UsbRawSendControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbControlRequestData *requestData);//發送同步USB控制傳輸請求
int32_t UsbRawSendBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);//發送同步USB批量傳輸請求
int32_t UsbRawSendInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);//發送同步執行USB中斷傳輸請求
int32_t UsbRawSubmitRequest(const struct UsbRawRequest *request);//提交非同步IO請求


感興趣的小伙伴可點擊下方鏈接查看完整的USB Host開發代碼: 

https://gitee.com/openharmony/drivers_peripheral/tree/master/usb/serial/src 

 

2)USB Device的開發


USB Device(設備端驅動)主要實現設備管理、配置管理、IO管理、數據通信等。USB Deivce DDK給開發者提供了設備創建、獲取介面、接收Event事件、收發數據等驅動能力介面,如圖5所示: 

 

圖5 USB Device DDK開放的API

 

下麵,我們將根據USB Deivce DDK提供的驅動能力介面來開發設備端驅動。 

 

1. 構造描述符

 

首先,需構造描述符來說明設備的總體信息。開發者可以通過設備功能代碼及設備私有數據HCS兩種途徑進行配置,下麵將分別介紹。 

 

(1) 在設備功能代碼中配置描述符,配置代碼如下: 

 

static struct UsbFnFunction g_acmFunction = {//功能描述符
    .enable         = true,
    .funcName       = "f_generic.a",
    .strings        = g_acmStrings,
    .fsDescriptors  = g_acmFsFunction,
    .hsDescriptors  = g_acmHsFunction,
    .ssDescriptors  = g_acmSsFunction,
.sspDescriptors = NULL,
};
struct UsbFnFunction *g_functions[] = {
#ifdef CDC_ECM
    &g_ecmFunction,
#endif
#ifdef CDC_ACM
    &g_acmFunction,
#endif
NULL
};
static struct UsbFnConfiguration g_masterConfig = {//配置描述符
    .configurationValue = 1,
    .iConfiguration     = USB_FUNC_CONFIG_IDX,
    .attributes         = USB_CFG_BUS_POWERED,
    .maxPower           = POWER,
    .functions          = g_functions,
};
static struct UsbFnConfiguration *g_configs[] = {
    &g_masterConfig,
    NULL,
};
static struct UsbDeviceDescriptor g_cdcMasterDeviceDesc = {//設備描述符
    .bLength            = sizeof(g_cdcMasterDeviceDesc),
    .bDescriptorType    = USB_DDK_DT_DEVICE,
    .bcdUSB             = CpuToLe16(BCD_USB),
    .bDeviceClass       = 0,
    .bDeviceSubClass    = 0,
    .bDeviceProtocol    = 0,
    .bMaxPacketSize0    = USB_MAX_PACKET_SIZE,
    .idVendor           = CpuToLe16(DEVICE_VENDOR_ID),
    .idProduct          = CpuToLe16(DEVICE_PRODUCT_ID),
    .bcdDevice          = CpuToLe16(DEVICE_VERSION),
    .iManufacturer      = USB_FUNC_MANUFACTURER_IDX,
    .iProduct           = USB_FUNC_PRODUCT_IDX,
    .iSerialNumber      = USB_FUNC_SERIAL_IDX,
    .bNumConfigurations = 1,
};
static struct UsbFnDeviceDesc g_masterFuncDevice = {//描述符入口
    .deviceDesc    = &g_cdcMasterDeviceDesc,
    .deviceStrings = g_devStrings,
    .configs       = g_configs,
};


(2) 在設備私有數據HCS中配置,配置代碼如下: 

 

root {
    module = "master";
master_config {
        match_attr         = "usbfn_master_driver";//該欄位與device中deviceMatchAttr
                                                             保持一致,否則無法找到的這個節點的信息。
    use_hcs            = 1;                         //用戶可以用該值決定是否使用hcs配置信息
    udc_name           = "100e0000.hidwc3_0";   //UDC的名字
        usb_dev_desc       = "UsbDeviceDescriptor";//設備描述符的節點UsbDeviceDescriptor
        usb_dev_string     = "UsbDeviceStrings";   //設備字元串的節點為UsbDeviceStrings
        usb_configuration = "UsbConfigs";           //配置描述符的節點為UsbConfigs
        ...
   }
}


設備描述符的節點為UsbDeviceDescriptor,配置如下: 

 

UsbDeviceDescriptor {
            bLength            = 18;
            bDescriptorType  = 0x01;
            bcdUSB             = 0x0200;
            bDeviceClass      = 0;
            bDeviceSubClass  = 0;
            bDeviceProtocol  = 0;
            bMaxPacketSize0  = 0x40;
            idVendor           = 0x0525;
            idProduct          = 0xA4A7;
            bcdDevice          = 0x0100;
            manufacturer       = 0;
            product             = 1;
            serialnumber       = 2;
            numConfigurations = 1; 
 }


2. 創建設備

 

描述符構造完成後,使用UsbFnDeviceCreate函數創建一個USB設備,並傳入UDC控制器名和UsbFnDescriptorData結構體。實現代碼如下: 

 

if (useHcs == 0) {//使用代碼編寫的描述符
        descData.type        = USBFN_DESC_DATA_TYPE_DESC;
        descData.descriptor = &g_acmFuncDevice;
    } else {             //使用hcs編寫的描述符
        descData.type         = USBFN_DESC_DATA_TYPE_PROP;
        descData.property    = acm->device->property;
}
   //創建設備
    fnDev = (struct UsbFnDevice *) UsbFnCreateDevice(acm->udcName, &descData);


3.獲取介面

 

設備創建後,使用UsbFnDeviceGetInterface函數獲取UsbInterface介面對象,並通過UsbFnGetInterfacePipeInfo函數獲取USB管道信息,實現代碼如下: 

 

//獲取介面
fnIface = (struct UsbFnInterface *)UsbFnGetInterface(fnDev, i);
//獲取Pipe信息
UsbFnGetInterfacePipeInfo(fnIface, i, &pipeInfo);
//獲取Handle
handle = UsbFnOpenInterface(fnIface);
//獲取控制(EP0)Request
req = UsbFnAllocCtrlRequest(acm->ctrlIface.handle,
            sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding));
//獲取Request
req = UsbFnAllocCtrlRequest(acm->ctrlIface.handle,
            sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding));


4. 接收Event事件

 

通過UsbFnStartRecvInterfaceEvent函數接收Event事件,並通過UsbFnEventCallback回調函數對Event事件做出響應,實現代碼如下: 

 

//開始接收Event事件
ret = UsbFnStartRecvInterfaceEvent(acm->ctrlIface.fn, 0xff, UsbAcmEventCallback, acm);
//Event處理回調函數
static void UsbAcmEventCallback(struct UsbFnEvent *event)
{
struct UsbAcmDevice *acm = NULL;


    if (event == NULL || event->context == NULL) {
        HDF_LOGE("%s: event is null", __func__);
        return;
    }


    acm = (struct UsbAcmDevice *)event->context;
    switch (event->type) {
        case USBFN_STATE_BIND:
            HDF_LOGI("%s: receive bind event", __func__);
            break;
        case USBFN_STATE_UNBIND:
            HDF_LOGI("%s: receive unbind event", __func__);
            break;
        case USBFN_STATE_ENABLE:
            HDF_LOGI("%s: receive enable event", __func__);
            AcmEnable(acm);
            break;
        case USBFN_STATE_DISABLE:
            HDF_LOGI("%s: receive disable event", __func__);
            AcmDisable(acm);
            acm->enableEvtCnt = 0;
            break;
        case USBFN_STATE_SETUP:
            HDF_LOGI("%s: receive setup event", __func__);
            if (event->setup != NULL) {
                AcmSetup(acm, event->setup);
            }
            break;
        case USBFN_STATE_SUSPEND:
            HDF_LOGI("%s: receive suspend event", __func__);
            AcmSuspend(acm);
            break;
        case USBFN_STATE_RESUME:
            HDF_LOGI("%s: receive resume event", __func__);
            AcmResume(acm);
            break;
        default:
            break;
    }
}


5. 收發數據

 

可以選擇同步非同步發送模式,實現代碼如下: 

 

notify = (struct UsbCdcNotification *)req->buf;
    ...
    if (memcpy_s((void *)(notify + 1), length, data, length)  != EOK) {
        return HDF_FAILURE;
    }
ret = UsbFnSubmitRequestAsync(req);//非同步發送


感興趣的小伙伴可點擊下方鏈接查看完整的設備測開發代碼。 

 

完整設備測開發代碼:https://gitee.com/openharmony/drivers_peripheral/tree/master/usb/gadget/function/ 

 

以上就是本期全部內容,通過本文的介紹相信你已經對USB DDK有了深刻的認識,期待廣大的開發者加入我們,一起豐富基於USB DDK的第三方驅動。

 

 

掃碼添加開發者小助手微信

獲取更多HarmonyOS開發資源和開發者活動資訊


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

-Advertisement-
Play Games
更多相關文章
  • 網易互聯網筆試(3.27) 網易互聯網3.27日筆試,四道筆試題一道簡答題,四道筆試題AK,簡答題考察設計模式不會。 第一道題模擬使用單體技能和群體技能攻擊怪物的場景、第二題字元串處理、第三題構造具有限制條件的完全二叉樹、第四題動態規劃問題。 題目 第一題 題意 給定兩個怪,分別具有a,b血量;給定 ...
  • 來源:https://zhenbianshu.github.io/ 將相似或重覆請求在上游系統中合併後發往下游系統,可以大大降低下游系統的負載,提升系統整體吞吐率。 文章介紹了 hystrix collapser、ConcurrentHashMultiset、自實現BatchCollapser 三種 ...
  • 人物背景: 老徐,男,本名徐福貴,從事Java相關研發工作多年,職場老油條,摸魚小能手,雖然歲數不大但長的比較著急,人稱老徐。據說之前炒某幣敗光了所有家產,甚至現在還有欠債。 阿珍,女,本名陳家珍,剛剛入職不久的實習生,雖然是職場菜鳥但聰明好學。據說是學校的四大校花之一,追求她的人從旺角排到了銅鑼灣 ...
  • 1. 可變字體 上圖中的兩個動畫,一個文字直接變粗,一個漸漸變粗,我覺得後者會更有趣。但普通的字體可達不到這種效果,例如微軟雅黑,無論怎麼調整它的 FontWeight,實際上它也只有三種粗細: 這時候我們需要可變字體,可變字體(Variable fonts)是OpenType字體規範上的演進,它允 ...
  • 1.現實中的問題 我們知道資料庫的數據,基本80%的業務是查詢,20%的業務涵蓋了增刪改,經過長期的業務變更和積累資料庫的數據到達了一定的數量之後,直接影響的是用戶與系統的交互,查詢時的速度,插入數據時的流暢度,系統的可用性,這些指標對用戶體驗都是會有影響的,不說用戶,你自己用是什麼感覺?我經歷過且 ...
  • cmd
    bat命令 cmd /E:ON 啟用命令擴展,off停用 net /?或help net 獲取幫助信息 命令格式:命令 子命令 參數 操作 選項 color設置顏色 title設置標題 mode 調視窗大小 位置參數%1,%2 A & B 都執行 && ,|| 短路 call 掉用其他bat sta ...
  • Linux 0.11源碼閱讀筆記-總覽 閱讀源碼的目的 加深對Linux操作系統的瞭解,瞭解Linux操作系統基本架構,熟悉進程管理、記憶體管理等主要模塊知識。 通過閱讀教複雜的代碼,鍛煉自己複雜項目代碼的閱讀能力。對於一般簡短的程式,可以從main函數逐行閱讀理解;對於複雜的項目,只能在瞭解源碼主要 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 1、簡介 官網:https://www.rabbitmq.com/ RabbitMQ是一個開源的遵循AMQP協議實現的基於Erlang語言編寫,支持多種客戶端(語言),用於在分散式系統中存儲消息,轉發消息,具有高可用高可擴性,易用性等特征。 2、 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...