




已阅读5页,还剩29页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
udhcp源码详解(一)之文件组织结构(dhcp server) -转udhcp目录下有十几个源文件,一个源文件相对应一个模块,完成一系列相关的功能,例如在static_leases.c主要针对static_lease链表增删查找等操作。dhcpd.c整个dhcp server运行的主线,server开始运行是从udhcpd_main函数开|始,相当于我们程序的main入口。在dhcpd_main将各个功能模块组合起|来实现我们的dhcp server。|arpping.c这个文件里只有一个函数arpping,server在给客户机分配IP地址|从IP地址池里找到a Free IP后,会调用arpping函数看网络上是否|主机使用该地址。|common.c引用文件的注释:Functions for debugging and logging as well as some|other simple helperfunctions.|file.c文件名可以看出她是跟文件打交道的。file.c里有个很重要的函数|read_config,读取默认配置信息和从配置文件udhcpd.conf文件里读|取配置信息,从网上Down个udhcpd.conf,她和Linux下其他配置文|件格式一样,以后写这类函数时候可以模仿着写。|leases.c针对在dhcpd.h文件里声明的struct dhcpOfferedAddr结构的操作函|数。|option.c在dhcp交互的数据报文中有个选项字段options,该文件里的函数操作|对象也就是选项信息。|serverpacket.c数据报文发送之前,根据不同情况对各个字段的填充相应的信息。|packet.cdhcp报文的发送信息封装与向网络发送。|pidfile.cFunctions to assist in the writing and removing of pidfiles.|主要针对/var/run/udhcpd.pid|signalpipe.cserver会监听两个文件描述符,其中一个是”管道”描述符,该描述符|的相应操作就在signalpipe.c文件里,另外还有对signal的设置|socket.csocket套接字创建,interface信息读取。|static_leases.c对在dhcpd.h里定义的struct static_lease结构体的相应操作函数。相对应得头文件是对函数声明,以及结构体的定义和宏的定义。udhcp详解源码(序)最近负责接入模块,包括dhcp、ipoe和pppoe等等。所以需要对dhcp和ppp这几个app的源代码进行一些分析。网上有比较好的文章,参考并补充自己的分析。这篇udhcp详解是基于busybox-1.20里的/busybox-1.20/networking/udhcp/目录下的源码进行讲解与分析,主要是对udhcp server端源码进行的讲解,client端相对于server端相对简单一些,源代码上面表现的实现思想大体相似。该系列的讲解分析是在源代码的基础上,所以并没有打算以单独章节来讲解DHCP协议工作交互流程,也就是已经了解掌握了DHCP协议,用C来实现而已。udhcp源码详解(二)-转定义的数据结构对于C程序的重要性,不言而喻。面向对象设计的程序是一个个对象的集合,而面向过程语言设计的程序则是数据结构与算法的集合。下面来分析的是dhcp server中的定义结构体:1)、在packet.h文件里定义了DHCP报文的格式:struct dhcpMessage报文的字段虽然都有注释,但还是有必要讲下options字段。options在大多文档中的说法是可选字段,大小不定,对于这个字段的重要性没有太多的强调。其实在DHCP交互过程,客户得到IP的配置过程,这个options字段有着很重要的作用,传递个很多不可或缺的信息。例如Server与Client交互时,数据包的类型,是通过该字段的指示的。还有Client要成功连接到互联网,不只是需要IP,还需要其他的配置信息,如Dns、Router、Subnet等信息,Server就是通过options字段把这些信息传递给Client。(查看options支持哪些选项信息可以查看文档RFC2132)问题来啦!这么多信息都放到一个字段,怎么合理的组织在一起呢,怎么能让交互双方准确的从这个字段取到相应的信息呢?options字段才用“CLV“方式组织数据信息,OPT_CODE:标识号,唯一标识后面的信息内容;占1byte;OPT_LEN:长度,表示后面信息内容的长度,占1byte;value(OPT_DATA):信息内容,其长度由OPT_LEN所指定,以byte为单位(RFC2132文档给出所有支持选项的OPT_CODE,和可以确定长度的OPT_LEN的值)CLV 的数据组织方式:0 1 2 +LengthCodeLengthDATA这是一种很漂亮的把多种数据信息组织在一个字段的方式,后面会看到对options字段相应的操作函数,这些函数就是根据CLV的方式对数据进行提取或者组织的。另外options字段存储的信息分为三大类: DHCP_PADDING 填充字节 /读取的信息时候注意跳过 DHCP_END 结束标志 /标志options字段的结束 CLV组织的有价值信息 /real value for usoptions字段还有个让人纠结的情况选项过载,其实也没什么,在后面遇再说吧!2)、在dhcpd.h里定义里一个贯穿整个Server端程序的结构体struct server_config_t上面的注释是源文件上的,本来的打算翻译下的,看了下注释很直白,没什么好翻译的,只是讲下其中的专业术语(好像是这么说的)。network order 网络字节序, host order 主机字节序 相信大家了解她们的区别吧。(定义类型是 uint32_t ,说明变量是以network order存储的常用数据类型 int long ,说明变量是以host order存储的)下面讲解下其中一些重要的成员: start,end可分配地址空间,每个客户的请求获得的IP都在这个内。IP地址池。 struct option_set结构体的定义也在dhcpd.h里Option set翻译过来:选项集合,就是的该结构的意义。上面分析struct dhcpMessage报文里有个选项字段options,她的值就是根据该集合(Option List)填写赋值的。集合(Option List)里每个结点是一个选项信息,数据CLV的组织方式的。与报文中options字段的区别是,报文里用一个options字段存储了所有的选项信息,options_set而是把一个一个的选项信息用链表链接起来。 下面的几个是与租赁期限有关的成员变量unsigned long lease client请求的租赁期限最大值,lease,就以lease为租赁期限;client请求未指明租赁期限,lease作为其租赁期限(静态租赁的默认租期)。unsigned long decline_time server的DHCPOFFER报文提供一个IP给client,client检测IP已经被其他主机使用,发送DHCPDECLINE报文给server,server接到该报文后,把IP添加到动态租赁数组里,租赁租期就是decline_time,对应的MAC为blank_chaddr(黑户,很形象-,实际值是全0)unsigned long confict_time server在IP地址池找个a free IP时,检测到IP已被网络中的主机所使用,会把IP添加到动态租赁数组里,租赁期限就是confict_time,MAC:blank_chaddr。unsigned long offer_time server发送DHCPOFFER报文时,即向client提供了IP地址,server会把IP和对应得MAC添加的动态租赁表里,但这个IP不一定会被client使用,所以添加到动态租赁表里的租赁期限要短,offer_time就是这个租赁期限(default : 60s)。(当server接收到DHCPREQUEST的时候会把租赁期限修改成请求的租赁期限)。unsigned long min_lease client端的请求租赁期限不能小于min_lease。 与保存租赁信息有关的两个成员变量:char remaining 摘自dhcpd.conf里的注释(以及翻译和注解):# If remaining is true (default), udhcpd will store the time# remaining for each lease in the udhcpd leases file. This is# for embedded systems that cannot keep time between reboots.# If you set remaining to no, the absolute time that the lease# expires at will be stored in the dhcpd.leases file.# 如果剩下的就是true(默认),udhcpd将存储时间文件中# 的每个udhcpd租赁租赁剩余。# 这对于嵌入式系统,不能保持在重新启动的时间。# (即重新启动就不算入租赁时间里)# 如果您设置其余为NO,绝对时间,# 租赁期满时将被储存在dhcpd.leases文件档案。# 绝对时间,例: starts 0 2000/01/30 08:02:54;# ends 5 2000/02/04 08:02:54;# 而嵌入式存储的是租赁剩余时间# 即 leasesi.expires - time(0) 的值unsigne long auto_time how long should udhcpd wait before writing a config file.if this is zero, it will only write one on SIGUSR1多长时间把动态租赁表里的信息写入文件(dhcpd.leases)里。Auto_time = 0的,只有等到SIGUSR1信号的时候才写。 struct static_lease *static_lease/* dhcpd.h */struct static_leaseuint8_t *mac;uint32_t *ip;struct static_lease *next;因为DHCP允许手动为client端配置IP,server端管理这些手动配置的IP就是使用该结构。在dhcpd.c文件里声明定义一个struct server_config_t的全局变量server_config,server对于client的响应交互都必须有这个变量的参与。3)、server端对于租赁出去的IP的管理基于以下这个结构体:uint8_t chaddr16; 客户机的 MAC地址;uint32_t yiaddr; 客户机租赁的IP地址;uint32_t expires; 客户机租赁IP的到期时间(是未来的一个时间点,是从1970.1.1午夜开始到租赁到期时刻的秒数)这里有些奇怪,使用uint32_t声明的expires存储方式用的是host order, 这是因为server在把租赁记录保存到dhcpd.leases文件时使用的是network order方式保存的。(个人认为声明为unsigned long类型更为合适)在dhcpd.c文件里声明定义一个 指向struct dhcpOferedAddr类型数组的全局指针变量leases。leases指向的数组大小由IP地址池大小决定的。Server端主要的结构体就是这些,他们是整个server端程序跑起来的基础。还有一些其他结构体的设计是为了某些函数特别定制的,在分析具体函数再做讲解。udhcp源码详解(三)上 之配置信息的读取上节介绍了存储管理配置信息的结构体struct server_config_t,该结构体贯穿整个server端程序的运行。在dhcpd.c里的用该结构体定义个一个全局的变量:struct server_config_t server_config;在server启动做的第一件事就是初始化这个全局变量server_config:现在就进入read_config函数,一睹其真容!学习这个函数,在以后遇到要读取配置信息可以参照read_config函数的写法。read_config函数的定义是在file.c文件里。分析read_config函数之前先来看一个结构体数组:结构体的定义:该结构体数组的定义以及初始化:结构体名和数组名可以看出,config_keyword,配置信息关键字,正如其名,是辅助读取配置信息的。数组里的每个config_keyword结构体元素中的keyword成员的值,与server_config_t里的成员变量名一一对应,该成员指示的就是配置的是server_config_t里的哪个成员。读取配置信息的操作,配置信息读取存储到哪里,以及配置信息的默认值,这些由config_keywork里的后面的3个成员给出的。成员heandler是一个函数指针,指向的函数即提供了怎么读取配置信息,以及把配置信息写到指定地址内。arg, def则作为handler指向函数的两个各参数,arg告诉函数把配置信息存储到的地址,def则是配置信息的值,def是配置信息的默认值。keyword数组里每个元素里的函数指针handler指向的这些函数,其定义都在file.c文件里。handler函数的类型 : int (const *handler)(const char *line, void *arg)1)read_ip:把字符串格式的IP地址转换成uint32_t的IP格式,并存储到arg所指的内存。2)read_u32:把字符串格式的数转换成uint32_t类型数字,并存储到arg所指的内存。3)read_yn: line字符串是“yes”还是 “no”?若“yes”,将arg所指的内存赋1,反之,赋04)read_str:首先free(*arg)释放掉*arg指的内存,再根据字符串line的大小分配内存,并把line里的值复制到里面,让*arg指向新分配的内存。5)read_opt:从字符串line里读取options写到*arg所指的struct option_set链表里。(链表的每个结点data以CLV方式组织数据)。read_opt对选项的信息的读取借助里也借助结构数组(在options.c里定义的),所以函数比较复杂,这里只用知道函数作用。6) read_staticlease:从line字符串里读取MAC、IP地址字符串里MAC与IP用/tor=作为分隔)再把MAC和IP添加到*arg所指的static_lease链表里。static_lease链表是dhcp Server管理已租赁出去的IP和获得该IP的客户机的MAC绑定。7) read_mac:从字符串格式MAC地址里读出MAC并转化的MAC存储arg所指的内存里上面的read_xxx函数,read成功返回1, 失败返回0。上面讲解的这么的多都是read_config函数的基础,好啦,现在正式进入read_config函数:若read_config函数到这就结束了,server依靠这些默认配置的值也可以工作(options字段没有默认值,不能给client其他的网络配置,但可以给client端分配IP的),但这样的话,程序就缺乏灵活性,IP地址池不能修改,租赁时间无法设定等。上面这段就是从文件里读取配置信息,下面是摘自udhcp.conf文件的部分配置信息:while(fgets(buffer, READ_CONFIG_BUF_SIZE, in)每次从文件里读取一行,存放在buffer里,在对buffer处理,空行、注释行(还有只有token, 没有line的)直接跳过继续读取下一行。token = strtok(buffer, “ /t”); 取到该行的keyword (文件里key与value之间使用空格或/t)line = strtok(NULL, “”); 取到该行的配置信息值这样(还需简单处理下)就可以根据keywords数组把从文件读取到的配置信息写到server_config相应的成员里。差不多了,总结下read_config函数,她对配置的对象全局变量server_conifg,通过一个辅助结构数组来对server_config里的成员变量进行赋值的。真正对于成员的赋值操作都是通过数组里元素的函数指针handler指向的函数进行的。read_config只是做了从文件的每行读出信息,并作些处理,得到token和line,利用token在数组里找到对应的handler和arg,udhcp源码详解(三) 下 之配置信息的读取上节讲解了read_config函数,读取配置信息到server_config的相应成员变量里,但read_config函数只负责把配置信息重文件里读出来,具体怎么把信息填写到指定的地址内,是调用keywords数组里元素的handler成员指向的函数完成。这节就是讲解完成具体的写入操作中比较复杂的函数。1) int read_opt(const char *line, void *arg)在read_config从udhcpd.conf里读到 token = “opt” 或 “option”;摘自udhcp.conf里的options配置信息调用handler: read_optconst_line的从udhcpd.conf的读出的lineline的值 例:dns 0subnet 等arg的值是 &(server_config.options)即是把line的值存储到头结点为*arg的链表里去。read_opt函数与read_config有点类似,也借助一个辅助的数组进行信息添加。在以前章节有讲解过选项信息的存储组织方式,CLV:Code + Length + Value,value我们文件里读出来了,Code和Length在哪?现在来看下这个辅助数组:元素结构体的定义:该结构体定义的数组:这个辅助数组的做法与read_config里的有些类似,name即是keyword,flag可以求出Length,code即Code。讲解这些函数真是痛苦,给你下套,而且是一环套一环的!得到了每个选项信息里的value,其值保存在opt指向的内存,value长度为length,Code在struct dhcp_option类型的option里。调用attach_option函数把选项信息的添加到server_config.options选项链表里。/* add an option to the opt_list* attach_option函数把从文件里读出的以options 或opt标识的配置信息* 在函数read_opt,通过option-flag处理好了line里val 值 存放在buffer* 再把配置信息填入到struct option_set的链表里* 参数:opt_list: &(server_config.options)* option: 根据选项头确定的dhcp_options数组里的一项* buffer 配置信息(去除了option/opt和选项头,而且已经转换过了)* length 配置信息的大小(以字节为单位)*/static void attach_option(struct option_set *opt_list, struct dhcp_option *option, char *buffer, int length);添加到链表里分为两种情况链表里没有与将要添加的选项信息的Code一样的结点,直接添加到链表即可。链表里已有与将要添加的选项信息的Code一样的结点,该Code选项信息flag选项里OPT_LIST(例如:dns选项的value是IP List 有两个IP):2)、int read_staticlease(const char *line, void *arg)udhcpd.conf里static lease:udhcp源码详解(四) 之租赁IP的管理Server端对于租赁出去的IP的管理是基于结构体dhcpOfferedAddr的,该结构体的定义是在leases.c文件里:(结构体的成员介绍说明见详解之数据结构)cppview plaincopy1. struct dhcpOfferedAddr 2. uint8_t chaddr16;3. uint32_t yiaddr; /* network order */4. uint32_t expires; /* host order */5. ;在dhcpd.c文件里用该结构体定义指向该结构数组的指针leases;/* dhcpd.c */#include struct dhcpOfferedAddr *leases;在读取完配置信息server_config后,就可以依据配置信息的内存为该结构数组申请内存空间了:cppview plaincopy1. /*2. * strut dhcpOfferedAddr 3. * uint8_t chaddr16;4. * uint32_t yiaddr; /network order5. * uint32_t expires; /host order6. * ;7. *8. * 该结构数组leases是记录租赁出去的IP, yiaddr9. * 租赁IP客户机的MAC, charddr10. * 租赁的到期时间,expires(从1970年1月1日00:00:00到租赁到期的总共的秒数)11. *12. * dhcp server启动后(可能是异常重启)13. * 所以要充server_config.file里读取上次为哪些客户机分配了IP14. * 并把读取到的内容添加到leases数组里15. */16. leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr);17. read_leases(server_config.lease_file);18. /server_config.lease_file = /var/lib/misc/udhcpd.leases2). 查找租赁最早到期的租约:cppview plaincopy1. /*2. * add a lease into the table, clearing out any old ones3. * add_lease是把MAC:chaddr, IP : yiaddr, expires: lease4. * 租赁信息添加到leases数组里5. * 函数首先调用clear_lease把数组里旧的删除掉(与chaddr or yiaddr相同的)6. * 从leases数组里找到最早到期的,再添加7. *8. * 返回oldest (NULL 表示未添加成功)9. */10. struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease)11. 12. struct dhcpOfferedAddr *oldest;13.14. /* clean out any old ones */15. clear_lease(chaddr, yiaddr);16.17. /* 从leases数组里找到一个最早到期的oldest,以便添加新的 */18. oldest = oldest_expired_lease();19.20. if (oldest) /oldest != NULL ,Found.21. 22. memcpy(oldest-chaddr, chaddr, 16);23. oldest-yiaddr = yiaddr;24.25. /*26. * 函数的第三个参数,即lease,表示的是租赁的剩余时间27. * 在动态租赁数组leases里记录的租赁到期时间28. *29. * 所以 oldest-expires = time(0) + lease;30. */31. oldest-expires = time(0) + lease;32. 33.34. return oldest;35. 3). 通过MAC或IP查找租约:cppview plaincopy1. /* Find the first lease that matches chaddr, NULL if no match */2. struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr)3. 4. unsigned int i;5.6. for (i = 0; i server_config.max_leases; i+)7. if (!memcmp(leasesi.chaddr, chaddr, 16) return &(leasesi);8.9. return NULL;10. 11.12. /* Find the first lease that matches yiaddr, NULL is no match */13. struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr)14. 15. unsigned int i;16.17. for (i = 0; i server_config.max_leases; i+)18. if (leasesi.yiaddr = yiaddr) return &(leasesi);19.20. return NULL;21. 4). 检查IP addr是否被网络中主机所使用,若被使用的话添加到leases数组里:cppview plaincopy1. /*2. * check is an IP is taken, if it is, add it to the lease table3. *4. * 检查addr is a free IP(网络中是否有主机使用addr)5. * if addr is a used IP, 把addr添加到租赁数组里6. *7. * 1. 返回0 表示addr 是a free IP8. * 2. 返回1表示addr已被网络中的某台主机使用了9. * 并且把使用addr的主机添加到leases数组里10. * 其中MAC: blank_chaddr, IP: addr, expires: server_config.confict_time(def: 3600sec)11. *12. */13. static int check_ip(uint32_t addr)14. 15. struct in_addr temp;16.17. /* arpping()返回0表示addr used */18. if (arpping(addr, server_config.server, server_config.arp, server_erface) = 0) 19. temp.s_addr = addr;20. LOG(LOG_INFO, %s belongs to someone, reserving it for %ld seconds,21. inet_ntoa(temp), server_config.conflict_time);22.23. /*24. * 因为刚在未在leases数组里找到这个已被used 的IP25. * 所以要把这个used IP添加到leases数组里26. * 租赁客户机MAC 为blank_chaddr(黑户)27. * 租赁的IP即used IP(addr)28. * 租赁时间:server_config.conflict_time29. */30. add_lease(blank_chaddr, addr, server_config.conflict_time);31. return 1;32. else return 0;33. 5) 从可用IP地址池里找到一个可用的IP(a Free IP)uint32_t find_address(int check_expire);该函数的调用是在,server端接收到DHCPDISOCVER的报文的时候,会为client提供一个IP地址:a) server首先利用client的MAC地址在leases数组里查找该client以前是否在这里租赁过IP,租赁过的话,把以前的IP提供给clientb) 第一种情况不满足的话,server会检查DHCPDISCOVER报文的选项字段,client是否有请求的IP(该选项信息的CODE :DHCP_REQUESTED_IP),有的话检查该IP是否为Free,可以的话把Request IP提供给client。c) 上面两种情况都不满足的话,就调用find_address这个函数了。cppview plaincopy1. /*2. * find an assignable address, it check_expired is true,3. * we check all the expired leases as well.4. * Maybe this should try expired leases by age.5. *6. * 找到一个可分配的IP,如果check_expired is true,7. * 会检查所有到期租约8. * find_address()函数找到free IP 返回IP的值(network order)9. * 未找到返回010. *11. */12. uint32_t find_address(int check_expired)13. 14. uint32_t addr, ret;15. struct dhcpOfferedAddr *lease = NULL;16.17. /*18. * 在端上进行计算比较的时候使用 host order19. * 一般存储的时候采用 network order20. *21. * 遍历整个可分配地址server_config.start - server_config.end22. */23. /* addr is in host order here */24. addr = ntohl(server_config.start);25. for (;addr = ntohl(server_config.end); addr+)26. 27. /* ie, */28. if (!(addr & 0xFF) continue;29.30. /* ie, 55 */31. if (addr & 0xFF) = 0xFF) continue;32.33. /*34. * Only do if it isnt35. * an assigned as a static lease36. * rexervedIP()检查addr是否在静态租赁链表里37. * addr在静态链表的话,函数返回1,反之返回038. */39. if(!reservedIp(server_config.static_leases, htonl(addr)40. 41. /* lease is not taken */42. ret = htonl(addr);43.44. /* !(lease = find_lease_by_yiaddr(ret) 地址addr是否已被租赁出去了 */45. if (!(lease = find_lease_by_yiaddr(ret) |46.47. /*48. * or it expired and we are checking for expired leases49. * or 租赁出去的话,50. * addr是否是a free IP还取决与51. * lease_expired(lease)是否已到期和check_expired52. *53. */54. (check_expired & lease_expired(lease) &55.56. /*57. * and it isnt on the network58. * 如果addr满足上面两个条件的其中一个59. * 1.!(lease = find_lease_by_yiaddr(ret) 没有被租赁出去 * 2.(check_expired & lease_expired(lease) 已被租赁出去了,60. * 但租赁过期了(check_expired = ture)61. *62. * addr 满足上面条件之一,还要检查addr是否被网络上其他主机使用63. *64. * check_ip() 发送arp Request包确认addr是否被使用65. * 返回0, addr is a free IP66. */67. !check_ip(ret)68. 69. return ret;70. break;71. 72. 73. 74. return 0;75. udhcp源码详解(四) 之租赁IP的管理Server端对于租赁出去的IP的管理是基于结构体dhcpOfferedAddr的,该结构体的定义是在leases.c文件里:(结构体的成员介绍说明见详解之数据结构) 1: struct dhcpOfferedAddr 2: uint8_t chaddr16; 3: uint32_t yiaddr; /* network order */ 4: uint32_t expires; /* host order */ 5: ;在dhcpd.c文件里用该结构体定义指向该结构数组的指针leases;/* dhcpd.c */#include struct dhcpOfferedAddr *leases;在读取完配置信息server_config后,就可以依据配置信息的内存为该结构数组申请内存空间了: 1: /* 2: * strut dhcpOfferedAddr 3: * uint8_t chaddr16; 4: * uint32_t yiaddr; /network order 5: * uint32_t expires; /host order 6: * ; 7: * 8: * 该结构数组leases是记录租赁出去的IP, yiaddr 9: * 租赁IP客户机的MAC, charddr 10: * 租赁的到期时间,expires(从1970年1月1日00:00:00到租赁到期的总共的秒数) 11: * 12: * dhcp server启动后(可能是异常重启) 13: * 所以要充server_config.file里读取上次为哪些客户机分配了IP 14: * 并把读取到的内容添加到leases数组里 15: */ 16: leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr); 17: read_leases(server_config.lease_file); 18: /server_config.lease_file = /var/lib/misc/udhcpd.leases 19: 2). 查找租赁最早到
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 美容美发店造型助手机器人企业制定与实施新质生产力项目商业计划书
- 瑜伽SPA融合课程创新创业项目商业计划书
- 2025年药物分析模拟试题含答案
- 2025年未成年人法规试题及答案
- 2025年LED室内应用灯具项目发展计划
- 高尺寸稳定性聚酰亚胺薄膜结构设计与制备技术研究
- 2025年滴眼剂机械项目合作计划书
- 2025年江西国家公务员行测考试真题及答案副省级
- 烯烃转换装置操作工岗前评优竞赛考核试卷含答案
- 铁基和锡基复合材料的制备及其电化学性能研究
- 【初中物理】《跨学科实践:为家庭电路做设计》课时教案-2025-2026学年人教版九年级全册物理
- 2025年国企中层干部竞聘考试试题及答案解析
- 2025年京东自营初级售前客服考试及答案2
- 江浙皖高中(县中)发展共同体2025-2026学年高三上学期10月联考化学试题(含答案)
- 安全生产费用投入台账明细
- 中国联通成都市2025秋招面试典型题目及答案
- 2025河南省文化旅游投资集团有限公司权属企业社会招聘52人笔试备考题库及答案解析
- 平凡心态面对挫折课件
- 幼儿园一日生活护理
- 2025年河北水利发展集团有限公司公开招聘工作人员41名笔试参考题库附带答案详解
- 胰岛素泵护理查房
评论
0/150
提交评论