【openwrt學習筆記】dnsmasq源碼閱讀

目錄

    • 一、DHCP(Dynamic Host Configuration Protocol)
      • 1.1 前置知識
      • 1.2 參考鏈接
      • 1.3 IP地址分配代碼分析
        • rfc2131.c
        • dhcp-common.c
        • dhcp.c
      • 1.4 幾個小問題
        • 1.4.1 連續IP模式(sequential_ip)
        • 1.4.2 重新連接使用IP地址
        • 1.4.3 續約租期
        • 1.4.4 不同的MAC地址分配到相同IP

一、DHCP(Dynamic Host Configuration Protocol)

1.1 前置知識

之前也學習了一下,總結了一些概念和抓包分析,此處不贅述。
DHCP和PPPoE協議以及抓包分析

1.2 參考鏈接

24-Openwrt dnsmasq
DNS and DHCP configuration
rfc2131文檔
DHCP協議詳解

1.3 IP地址分配代碼分析

本次著重看了這一塊代碼,其它部分再后續補充。
吐槽一下:源代碼的格式真是一言難盡,縮進亂七八糟,而且有的空格有的tab看著也難受,格式不標準,有的都不能正確縮放,還是讓GPT轉化了一下再看的。

rfc2131.c

if (mess_type == 0 && !pxe) {/* BOOTP request */struct dhcp_netid id, bootp_id;struct in_addr *logaddr = NULL;/* must have a MAC addr for bootp */if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))return 0;if (have_config(config, CONFIG_DISABLE))message = _("disabled");end = mess->options + 64; /* BOOTP vend area is only 64 bytes *///如果配置中設置了 CONFIG_NAME 標志,則設置主機名(hostname)和域名(domain)。if (have_config(config, CONFIG_NAME)) {hostname = config->hostname;domain = config->domain;}
//遍歷配置中的網絡標識列表(netid),并將它們添加到當前的 netid 列表中if (config) {struct dhcp_netid_list *list;for (list = config->netid; list; list = list->next) {list->list->next = netid;netid = list->list;}}/* Match incoming filename field as a netid. */if (mess->file[0]) {memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */id.net = (char *)daemon->dhcp_buff2;id.next = netid;netid = &id;}/* Add "bootp" as a tag to allow different options, address ranges etc for BOOTP clients */bootp_id.net = "bootp";bootp_id.next = netid;netid = &bootp_id;//運行標識的處理函數(run_tag_if)以獲取最終的 netid 列表。tagif_netid = run_tag_if(netid);//遍歷 dhcp_ignore 列表,如果與 netid 列表中的標識匹配,則將消息設置為 "ignored"。for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next) {if (match_netid(id_list->list, tagif_netid, 0)) {message = _("ignored");break;}}if (!message) {int nailed = 0;
//檢查是否已配置 IP 地址(have_config(config, CONFIG_ADDR))。如果配置了,嘗試分配指定的 IP 地址。if (have_config(config, CONFIG_ADDR)) {nailed = 1;logaddr = &config->addr;mess->yiaddr = config->addr;if ((lease = lease_find_by_addr(config->addr)) &&(lease->hwaddr_len != mess->hlen ||lease->hwaddr_type != mess->htype ||memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0)){message = _("address in use");}}else {
//如果沒有配置 IP 地址,嘗試從已分配的地址中查找與客戶端 MAC 地址匹配的租約(lease_find_by_client)if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||!address_available(context, lease->addr, tagif_netid)){if (lease) {/* lease exists, wrong network. */lease_prune(lease, now);lease = NULL;}
//如果沒有找到匹配的租約或該地址不可用(address_available),則嘗試分配一個新的 IP 地址if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback)) {message = _("no address available");}}else {mess->yiaddr = lease->addr;}}//如果分配了 IP 地址,檢查該地址是否在正確的網絡上(narrow_context)。如果不在正確的網絡上,設置錯誤消息為 "wrong network"。if (!message && !(context = narrow_context(context, mess->yiaddr, netid))) {message = _("wrong network");}//更新 netid 列表并重新運行標識處理函數(run_tag_if)。else if (context->netid.net) {context->netid.next = netid;tagif_netid = run_tag_if(&context->netid);}log_tags(tagif_netid, ntohl(mess->xid));//遍歷 bootp_dynamic 列表,檢查是否配置了與 netid 列表匹配的地址。如果沒有找到,則設置錯誤消息為 "no address configured"。if (!message && !nailed) {for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next) {if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0)) {break;}}if (!id_list) {message = _("no address configured");}}//如果沒有錯誤消息,并且沒有租約可用(!lease),嘗試為分配的 IP 地址創建一個租約(lease4_allocate)。if (!message && !lease && !(lease = lease4_allocate(mess->yiaddr))) {message = _("no leases left");}//如果成功分配租約,設置租約的硬件地址、主機名、租約到期時間等信息。if (!message) {logaddr = &mess->yiaddr;lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);if (hostname) {lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);}/* infinite lease unless nailed in dhcp-host line. */lease_set_expires(lease,  have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, now);lease_set_interface(lease, int_index, now);//清空消息中的選項(do_options),為客戶端提供配置選項。clear_packet(mess, end);do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);}}daemon->metrics[METRIC_BOOTP]++;log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
}

然后看一個各個函數的具體實現:

dhcp-common.c

//根據匹配條件更新給定的網絡標識列表,并重新運行標識處理邏輯。
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{struct tag_if *exprs;struct dhcp_netid_list *list;// 遍歷服務器中定義的標識處理表達式(tag_if)for (exprs = daemon->tag_if; exprs; exprs = exprs->next) {// 檢查當前標識處理表達式是否與當前網絡標識列表匹配if (match_netid(exprs->tag, tags, 1)) {// 對于匹配的表達式,遍歷操作集合(set)for (list = exprs->set; list; list = list->next) {// 將操作列表中的操作添加到網絡標識列表(tags)的開頭list->list->next = tags;tags = list->list;}}}// 返回更新后的網絡標識列表(tags)return tags;
}

dhcp.c

struct dhcp_context *address_available(struct dhcp_context *context,struct in_addr taddr,struct dhcp_netid *netids)
{/* 檢查地址是否適用于此網絡,檢查所有可能的范圍。確保該地址沒有被服務器自身使用。 */unsigned int start, end, addr = ntohl(taddr.s_addr);struct dhcp_context *tmp;// 檢查提供的地址是否與服務器的路由器地址匹配for (tmp = context; tmp; tmp = tmp->current) {if (taddr.s_addr == context->router.s_addr) {return NULL; // 服務器的路由器地址,不可用}}// 遍歷每個上下文及其地址范圍,以查找可用的地址for (tmp = context; tmp; tmp = tmp->current) {start = ntohl(tmp->start.s_addr);end = ntohl(tmp->end.s_addr);// 檢查地址是否在當前上下文的范圍內且匹配給定的網絡標識if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&addr >= start &&addr <= end &&match_netid(tmp->filter, netids, 1)) {return tmp; // 地址在此上下文中可用}}return NULL; // 地址不可用
}
int address_allocate(struct dhcp_context *context,struct in_addr *addrp, unsigned char *hwaddr, int hw_len,struct dhcp_netid *netids, time_t now, int loopback)
{/* 尋找一個可用的地址:排除正在使用的地址和分配給特定硬件地址/客戶端標識/主機名的地址首先嘗試返回與netids匹配的上下文。 */struct in_addr start, addr;struct dhcp_context *c, *d;int i, pass;unsigned int j;/* 對hwaddr進行哈希:使用SDBM哈希算法。即使在具有類似值的“字符串”上,似乎也能得到良好的分散效果。 */for (j = 0, i = 0; i < hw_len; i++)j = hwaddr[i] + (j << 6) + (j << 16) - j;/* j == 0 是標記 */if (j == 0)j = 1;for (pass = 0; pass <= 1; pass++)for (c = context; c; c = c->current) {if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))continue;else if (!match_netid(c->filter, netids, pass))continue;else {if (option_bool(OPT_CONSEC_ADDR))/* 種子是此上下文中最大的現存租約地址 */start = lease_find_max_addr(c);else/* 基于hwaddr選擇種子 */start.s_addr = htonl(ntohl(c->start.s_addr) +((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));/* 循環,直到找到一個可用的地址。 */addr = start;do {/* 排除服務器正在使用的地址。 */for (d = context; d; d = d->current) {if (addr.s_addr == d->router.s_addr) {break;}}/* 以.255和.0結尾的地址在Windows上有問題,即使使用超網也是如此。例如,dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0那么192.168.0.255是有效的IP地址,但在Windows上卻不是,因為它在C類范圍內。請參閱KB281579。因此,我們不分配這些地址,以避免難以診斷的問題。感謝Bill。 */if (!d &&!lease_find_by_addr(addr) &&!config_find_by_address(daemon->dhcp_conf, addr) &&(!IN_CLASSC(ntohl(addr.s_addr)) ||((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0)))) {/* 在連續IP模式下,跳過等于客戶端拒絕的地址數量的地址。這應該避免同一客戶端在拒絕地址后再次被分配相同的地址。 */if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch) {c->addr_epoch--;} else {struct ping_result *r;if ((r = do_icmp_ping(now, addr, j, loopback))) {/* 連續IP模式:我們最近為另一個客戶端提供了此地址(不同的哈希),不要再次提供給此客戶端。 */if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j) {*addrp = addr;return 1;}} else {/* 地址正在使用中:擾動地址選擇,以便不太可能再次嘗試此地址。 */if (!option_bool(OPT_CONSEC_ADDR)) {c->addr_epoch++;}}}}addr.s_addr = htonl(ntohl(addr.s_addr) + 1);if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1)) {addr = c->start;}} while (addr.s_addr != start.s_addr);}}return 0; // 無可用地址
}

上述算法過程舉例如下,假設有一個 DHCP 上下文 c,其中包含以下信息:

  • c->start.s_addr 表示分配地址的起始地址,假設為 192.168.1.100(以網絡字節序表示)。
  • c->end.s_addr 表示分配地址的結束地址,假設為 192.168.1.200(以網絡字節序表示)。
  • c->addr_epoch 是一個地址時代值,假設為 5。
  • j 是之前計算得到的 SDBM 哈希值,假設為 12345。

現在我們來演示如何通過上述代碼生成一個新的分配地址:

  1. 計算地址范圍內的總地址數:1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr) = 1 + 192.168.1.200 - 192.168.1.100 = 101

  2. 計算 SDBM 哈希值和地址時代的影響:(j + c->addr_epoch) % (1 + 101) = (12345 + 5) % 102 = 12350 % 102 = 46

  3. 計算新的分配地址的偏移量:ntohl(c->start.s_addr) + 46 = 3232235876 + 46 = 3232235922

  4. 將計算得到的偏移量轉換為網絡字節序:htonl(3232235922) = 192.168.1.122

因此,根據上述計算,生成的新分配地址將是 192.168.1.122。這個過程確保了生成的地址在指定的地址范圍內,同時通過 SDBM 哈希值和地址時代進行了調整。

struct dhcp_context *narrow_context(struct dhcp_context *context,struct in_addr taddr,struct dhcp_netid *netids)
{/* 我們從一組可能的上下文開始,所有這些上下文都在當前的物理接口上。這些上下文通過 ->current 進行鏈接。在這里,我們有一個地址,并返回與該地址對應的實際上下文。請注意,如果地址來自dhcp-host,并且位于任何dhcp-range之外,則可能沒有匹配的上下文。在這種情況下,如果可能的話,我們會返回靜態范圍,或者如果失敗的話,返回正確子網上的任何上下文。(如果有多個上下文,這是一個不穩定的配置:也許應該有一個警告。) */struct dhcp_context *tmp;if (!(tmp = address_available(context, taddr, netids))) {for (tmp = context; tmp; tmp = tmp->current) {if (match_netid(tmp->filter, netids, 1) &&is_same_net(taddr, tmp->start, tmp->netmask) &&(tmp->flags & CONTEXT_STATIC)) {break;}}if (!tmp) {for (tmp = context; tmp; tmp = tmp->current) {if (match_netid(tmp->filter, netids, 1) &&is_same_net(taddr, tmp->start, tmp->netmask) &&!(tmp->flags & CONTEXT_PROXY)) {break;}}}}/* 現在只允許一個上下文 */if (tmp) {tmp->current = NULL;}return tmp;
}

narrow_context 的函數,用于根據給定的地址和網絡標識來縮小上下文的范圍。它首先嘗試根據給定的地址和網絡標識來查找可用的上下文。如果找不到匹配的可用上下文,則會嘗試在同一子網中查找靜態范圍,或者如果沒有靜態范圍,則查找任何與正確子網匹配的上下文。最后,函數會將選定的上下文鏈表中的 current 指針設置為 NULL,以確保只返回一個上下文。函數將返回選定的上下文,如果找不到合適的上下文,則返回 NULL。

1.4 幾個小問題

1.4.1 連續IP模式(sequential_ip)

上述代碼中有提到這個模式,openwrt中對應的配置字段就是sequential_ip。對應的描述如下:

NameTypeDefaultOptionDescription
sequential_ipboolean0–dhcp-sequential-ipDnsmasq is designed to choose IP addresses for DHCP clients using a hash of the client’s MAC address. This normally allows a client’s address to remain stable long-term, even if the client sometimes allows its DHCP lease to expire. In this default mode IP addresses are distributed pseudo-randomly over the entire available address range. There are sometimes circumstances (typically server deployment) where it is more convenient to have IP addresses allocated sequentially, starting from the lowest available address, and setting this parameter enables this mode. Note that in the sequential mode, clients which allow a lease to expire are much more likely to move IP address; for this reason it should not be generally used.
Dnsmasq用于使用客戶端MAC地址的哈希為DHCP客戶端選擇IP地址。這通常允許客戶端的地址長期保持穩定,即使客戶端有時允許其DHCP租約到期。在此默認模式下,IP地址在整個可用地址范圍內偽隨機分布。有時在某些情況下(通常是服務器部署),從最低可用地址開始按順序分配IP地址更方便,并且設置此參數可以啟用此模式。請注意,在順序模式中,允許租約到期的客戶端更有可能移動IP地址;由于這個原因,它不應該被普遍使用。

在這里插入圖片描述

可以看到按順序分配,設置成功
在這里插入圖片描述

1.4.2 重新連接使用IP地址

在RFC 2131文檔第3.2節描述了客戶端重新使用先前分配的網絡地址時的客戶端-服務器交互過程:

根據文檔中的描述,如果客戶端希望重新連接并使用先前分配的網絡地址,客戶端可以選擇省略先前部分描述的一些步驟。客戶端可以通過向服務器發送DHCPREQUEST消息來請求使用先前分配的網絡地址。在DHCPREQUEST消息中,客戶端將其網絡地址填入“請求的IP地址”選項中,并且不填寫“ciaddr”字段。服務器收到DHCPREQUEST消息后,如果具有客戶端的配置參數信息,則會向客戶端發送DHCPACK消息服務器不應檢查客戶端的網絡地址是否已被使用。客戶端在收到DHCPACK消息后,將配置參數應用于自身,并記錄DHCPACK消息中指定的租約持續時間。此時,客戶端已經重新連接并配置完成。

請注意,根據文檔中的描述,如果客戶端在DHCPACK消息中檢測到分配的IP地址已經被使用,則客戶端必須發送DHCPDECLINE消息給服務器,并重新啟動配置過程以請求新的網絡地址。如果客戶端收到DHCPNAK消息,則表示無法重新使用先前分配的網絡地址,客戶端必須重新啟動配置過程,并按照文檔中描述的完整流程進行操作。

在這里插入圖片描述
重用以前分配的網絡地址時,DHCP客戶端和服務器之間交換的消息時間軸圖

注:客戶端通常不會在正常關閉期間放棄其租約。只有在客戶端明確需要放棄其租期的情況下,例如,客戶端即將移動到另一個子網,客戶端才會發送DHCPRELEASE消息。

1.4.3 續約租期

在RFC 2131文檔的第4.4.5節中描述了租期續約的過程:

  1. 客戶端在T1之前選擇續約或延長租期:客戶端可以選擇在T1之前續約或延長租期。這意味著客戶端可以在租期即將到期之前主動向服務器發送DHCPREQUEST消息來請求續約。

  2. 服務器根據網絡管理員設置的策略來決定是否延長租期:服務器可以根據網絡管理員設置的策略來決定是否延長客戶端的租期。這意味著服務器可以根據自己的策略來決定是否接受客戶端的續約請求。

  3. 服務器應返回調整后的T1和T2的值:服務器應返回調整后的T1和T2的值,以考慮租期剩余的時間。這意味著服務器應根據租期剩余時間來調整T1和T2的值,并將其返回給客戶端。

  4. 在續約和重新綁定狀態下,如果客戶端收不到DHCPREQUEST消息的響應,客戶端應等待剩余時間的一半(在續約狀態下是T2的一半,在重新綁定狀態下是租期剩余時間的一半),直到最少等待60秒,然后重新發送DHCPREQUEST消息。

具體有以下三種情況:
(1)當clientIP地址已經用到50%的時間,續租一下,client端就會以單播形式向服務端發送一個DHCP Request包,當server響應時就會回應一個ACK包,會重新約定一個時間。

(2)當clientIP地址已經用到50%的時間,續租一下,client端就會以單播形式向服務端發送一個DHCP Request包,server沒有響應,client會繼續使用,當使用到87.5%時,會在續租一次,同時就以廣播的方式是發送一個request包,server這時收到響應以后,就會回應一個ACK包,重新約定一個時間。

(3)當clientIP地址已經用到50%的時間,續租一下,client端就會以單播形式向服務端發送一個DHCP Request包,server沒有響應,client會繼續使用,當使用到87.5%時,會在續租一次,同時就以廣播的方式是發送一個request包,如果server還是沒有響應,client那就直接使用到過期。

在這里插入圖片描述
DHCP客戶端的狀態轉換圖

1.4.4 不同的MAC地址分配到相同IP

這是無意間收到的一個提問,可以參考學習。
multiple offers with same IP to different MAC addresses

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

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

相關文章

VS+Qt+C++旅游景區地圖導航源碼實例

程序示例精選 VSQtC旅游景區地圖導航 如需安裝運行環境或遠程調試&#xff0c;見文章底部個人QQ名片&#xff0c;由專業技術人員遠程協助&#xff01; 前言 這篇博客針對<<VSQtC旅游景區地圖導航>>編寫代碼&#xff0c;代碼整潔&#xff0c;規則&#xff0c;易讀。…

【Vue框架】菜單欄權限的使用與顯示

前言 在 【Vue框架】Vue路由配置 中的getters.js里&#xff0c;可以看到有一個應用程序的狀態&#xff08;變量&#xff09;叫 permission_routes&#xff0c;這個就是管理前端菜單欄的狀態。具體代碼的介紹&#xff0c;都以注釋的形式來說明。 1、modules\permission.js 1…

SpringBoot 將項目打包成 jar 包

SpringBoot 將項目打包成 jar 包 一、項目打包成 jar 包 首先在 pom.xml 文件中導入 Springboot 的 maven 依賴 <!-- 將應用打包成一個可以執行的 jar 包 --> <build><plugins><plugin><groupId>org.springframework.boot</groupId><…

學習筆記整理-面向對象-02-認識函數的上下文

一、認識函數的上下文 什么是上下文 垃圾分類&#xff0c;這是非常好的習慣&#xff0c;值得表揚隨手關燈&#xff0c;這是非常好的習慣&#xff0c;值得表揚遛狗栓繩&#xff0c;這是非常好的習慣&#xff0c;值得表揚課后復習&#xff0c;這是非常好的習慣&#xff0c;值得…

【數據結構】單鏈表OJ題(二)

&#x1f525;博客主頁&#xff1a;小王又困了 &#x1f4da;系列專欄&#xff1a;數據結構 &#x1f31f;人之為學&#xff0c;不日近則日退 ??感謝大家點贊&#x1f44d;收藏?評論?? 目錄 一、鏈表分割 &#x1f4a1;方法一&#xff1a; 二、鏈表的回文 &#x…

hosts文件中被添加 windows10.microdone.cn

在網上搜了一圈逗說是之前下過征信中心的安全控件,是微通新成網絡科技有限公司這家公司提供的,也是http://microdone.cn的運營商。后邊只要使用代理,就會跳出來,所以常規處理操作就是去把瀏覽器上的安全控件卸載了。 參考 解決 windows10 的 代理頻繁被自動篡改為windows10.mi…

利用python實現激光雷達LAS數據濾波的7種方式,使用laspy讀寫

激光雷達&#xff08;LiDAR&#xff09;數據在實際應用中可能受到噪聲和不完美的測量影響&#xff0c;因此數據去噪和濾波方法變得至關重要&#xff0c;以提高數據質量和準確性。以下是一些常用的激光雷達數據去噪與濾波方法。 原始數據如下&#xff1a; 1. 移動平均濾波&…

kubernetes中PV和PVC

目錄 一、PV、PVC簡介 二、PV、PVC關系 三、創建靜態PV 1.配置nfs存儲 2.定義PV 3.定義PVC 4.測試訪問 四、 搭建 StorageClass nfs-client-provisioner &#xff0c;實現 NFS 的動態 PV 創建 1. 配置nfs服務 2.創建 Service Account 3.使用 Deployment 來創建 NFS P…

Figma中文社區來啦,云端協作設計你準備好了嗎?

Figma是改變產品設計協作方式的重要工具,但由于沒有中文社區,對國內設計師的約束較大。而擁有全中文UI 界面、功能齊全的即時設計資源廣場,恰好彌補了Figma的這一短板,它也將取代Figma成為設計師新寵。 1、UI組件集 Figma中文社區替代即時設計資源廣場,擁有海量豐富的UI設計組…

【BEV Review】論文 Delving into the Devils of Bird’s-eye-view 2022-9 筆記

背景 一般來說&#xff0c;自動駕駛車輛的視覺傳感器&#xff08;比如攝像頭&#xff09;安裝在車身上方或者車內后視鏡上。無論哪個位置&#xff0c;攝像頭所得到的都是真實世界在透視視圖&#xff08;Perspective View&#xff09;下的投影&#xff08;世界坐標系到圖像坐標系…

ssm柚子云電子商城java圖書購物電子商務管理jsp源代碼

本項目為前幾天收費幫學妹做的一個項目&#xff0c;Java EE JSP項目&#xff0c;在工作環境中基本使用不到&#xff0c;但是很多學校把這個當作編程入門的項目來做&#xff0c;故分享出本項目供初學者參考。 一、項目描述 ssm柚子云電子商城 系統有2權限&#xff1a;前臺、后…

SpringBoot筆記:SpringBoot 集成 Dataway 多數據源配置(二)

文章目錄 前言核心代碼和配置yml 配置注入多數據源常用Spi實現swagger 配置自定義 Udf指定數據源進行查詢 前言 之前簡單介紹了一下 Dataway 使用&#xff0c;本文繼續介紹一下它的多數據源配置和使用。 核心代碼和配置 yml 配置 # springboot多環境配置 #端口&#xff0c;…

JavaScript應用:五子棋游戲實戰開發

&#x1f3c6;作者簡介&#xff0c;黑夜開發者&#xff0c;全棧領域新星創作者?&#xff0c;CSDN博客專家&#xff0c;阿里云社區專家博主&#xff0c;2023年6月csdn上海賽道top4。 &#x1f3c6;數年電商行業從業經驗&#xff0c;歷任核心研發工程師&#xff0c;項目技術負責…

面試熱題(螺旋矩陣)

給你一個 m 行 n 列的矩陣 matrix &#xff0c;請按照 順時針螺旋順序 &#xff0c;返回矩陣中的所有元素 一看到這個大家有沒有想到 就是一個螺旋形狀&#xff0c;那這道題我們應該怎么解決&#xff1f; 我們先來仔細的看&#xff0c;它這種螺旋形狀的遍歷是先【右-下-左-上】…

Docker中Tomcat部署步驟

第一次訪問沒有東西。

為什么我不推薦任何人用C語言作為編程啟蒙第一課?

前言 寫了20多年的代碼&#xff0c;之前做過阿里的高級架構師&#xff0c;在技術這條路上跌跌撞撞了很多&#xff0c;我今天分享一些我個人的自學方法給各位。為什么我會說&#xff1a;不推薦任何人用C語言作為編程啟蒙第一課&#xff1f; 這里有很多同學要站出來說了&#x…

實現CP指令

一、文件的打開創建 #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(const char *pathname, int flags); flags: O_RDONLY 只讀 O_WRONLY 只寫 O_RDWR 可讀可寫 int open(const char *pathname, int flags, mode_t mode); 如果 …

VsCode美化 - VsCode自定義 - VsCode自定義背景圖

VsCode美化 - VsCode自定義 - VsCode自定義背景圖&#xff1a;添加二次元老婆圖到VsCode 前言 作為一個二刺螈&#xff0c;VsCode用久了&#xff0c;總覺得少了些什么。是啊&#xff0c;高效的代碼生產工具中怎么能沒有老婆呢&#xff1f; 那就安裝一個VsCode插件把老婆添加…

章節7:Burp Intruder模塊

章節7&#xff1a;Burp Intruder模塊 參考資料 https://portswigger.net/burp/documentation/desktop/tools/intruder 01 Intruder模塊作用與原理 原理 http://xxx.xx.com/bbs/index.php?namewuyanzu&mottogo 對請求參數進行修改&#xff0c;分析響應內容&#xff0…

Linux 內核第一版 (v0.01) 開源代碼解讀

探索Linux v0.01的內部結構&#xff0c;Linux內核經常被認為是一個龐大的開源軟件。在撰寫本文時&#xff0c;最新版本是v6.5-rc5&#xff0c;包含36M行代碼。不用說&#xff0c;Linux是幾十年來許多貢獻者辛勤工作的成果。 Linux 內核首個開源版本 (v0.01) 的體積非常小&…