版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第4章NetBIOS網(wǎng)絡(luò)編程4.1概述4.2NetBIOS應(yīng)用服務(wù)與實(shí)現(xiàn)4.3數(shù)據(jù)報(bào)通信程序設(shè)計(jì)4.4會(huì)話通信程序設(shè)計(jì)小結(jié)
NetBIOS是1983年IBM為PC-Network開(kāi)發(fā)的一套網(wǎng)絡(luò)標(biāo)準(zhǔn),由于NetBIOSAPI具有在面向連接或非連接的通信過(guò)程中,應(yīng)用程序都能通過(guò)它訪問(wèn)傳輸層聯(lián)網(wǎng)協(xié)議;再加上NetBIOS協(xié)議短小精悍,非常適用于實(shí)時(shí)性要求較高的小型LAN網(wǎng)絡(luò)環(huán)境;以及其良好的網(wǎng)絡(luò)通信性能,因而NetBIOS一經(jīng)推出就成為L(zhǎng)AN接入服務(wù)的標(biāo)準(zhǔn)接口協(xié)議。雖然NetBIOS不是一種可路由的協(xié)議,數(shù)據(jù)包無(wú)法跨網(wǎng)段傳輸,已漸漸成為網(wǎng)絡(luò)通信的次要協(xié)議,但其編程思想明晰簡(jiǎn)要,且與后續(xù)的編程協(xié)議基本一致,因此依然活躍在當(dāng)前的網(wǎng)絡(luò)通信中?;谏鲜鲈颍W(wǎng)絡(luò)編程的初學(xué)者則有必要學(xué)習(xí)NetBIOS協(xié)議。
NetBIOS是早期的網(wǎng)絡(luò)通信協(xié)議,起初NetBIOS駐留在LANA型網(wǎng)絡(luò)適配器(adapter)的擴(kuò)展BIOSROM中,隨后IBM開(kāi)發(fā)了NETBIOS.COM、令牌環(huán)NETBEUI以及IBMLAN支持程序,以適應(yīng)不斷發(fā)展的網(wǎng)絡(luò)通信需求。微軟在IBMNetBIOS的基礎(chǔ)上進(jìn)行了更深入的開(kāi)發(fā),并將NetBIOS相繼應(yīng)用到其陸續(xù)推出的Windows系列產(chǎn)品中,對(duì)NetBIOS的廣泛使用起到了極大的推動(dòng)作用。4.1概述可以這樣說(shuō),微軟早期的客戶(hù)機(jī)/服務(wù)器網(wǎng)絡(luò)系統(tǒng)就是基于NetBIOS的網(wǎng)絡(luò),WindowsNT操作系統(tǒng)中大量的內(nèi)部聯(lián)網(wǎng)工作也都是利用NetBIOS來(lái)完成的。除此之外,微軟還為許多其他協(xié)議提供了標(biāo)準(zhǔn)的NetBIOS界面,如TCP/IP(NetBIOS設(shè)置見(jiàn)圖4-1)、NetBEUI和NWLink,使NetBIOS的應(yīng)用更加方便。
目前,NetBIOS最廣泛的應(yīng)用之一就是對(duì)NetBIOS用戶(hù)擴(kuò)展接口(NetBIOSExtendUserInterface,NetBEUI)協(xié)議的使用,在WindowsXP中對(duì)NetBEUI協(xié)議的添加如圖4-2所示。NetBEUI可以看做是NetBIOS協(xié)議的延伸及改良,具有體積小、效率高以及速度快等特點(diǎn),運(yùn)行在標(biāo)準(zhǔn)802.2數(shù)據(jù)鏈協(xié)議層上。NetBEUI規(guī)范了在NetBIOS中未標(biāo)準(zhǔn)化的傳輸幀,并加入了額外功能。NetBEUI協(xié)議同樣是一種非路由協(xié)議(路由器會(huì)將它收到的非路由協(xié)議數(shù)據(jù)包統(tǒng)統(tǒng)丟棄),主要用于本地局域網(wǎng)中,一般不能與其他網(wǎng)絡(luò)的計(jì)算機(jī)進(jìn)行溝通,這一點(diǎn)不同于TCP/IP、IPX/SPX協(xié)議。
隨著PC-Network被令牌環(huán)和以太網(wǎng)取代,以及NetBIOS不具備路由功能等原因,在廣域網(wǎng)、城域網(wǎng)被廣泛應(yīng)用的今天,NetBIOS已經(jīng)逐漸退居配角。但是,由于NetBIOS被適配到了各種其他協(xié)議上,如IPX/SPX和TCP/IP,目前仍然有很多軟件的開(kāi)發(fā)在使用NetBIOSAPI。同時(shí),鑒于NetBIOSAPI的高速、編程接口與協(xié)議無(wú)關(guān)等優(yōu)點(diǎn),還有相當(dāng)部分網(wǎng)絡(luò)應(yīng)用需要用到NetBIOS,如瀏覽網(wǎng)上鄰居和共享文件等,所以NetBIOS依然活躍于網(wǎng)絡(luò)編程設(shè)計(jì)工作中。
圖4-1在Windows2000/XP中設(shè)置NetBIOS
圖4-2在Windows2000/XP中添加NetBEUI協(xié)議
4.2.1NetBIOS應(yīng)用服務(wù)
NetBIOS程序的網(wǎng)絡(luò)通信功能主要是通過(guò)名字服務(wù)、數(shù)據(jù)報(bào)服務(wù)、會(huì)話服務(wù)及一般命令服務(wù)這四種NetBIOS應(yīng)用服務(wù)來(lái)實(shí)現(xiàn)的。在整個(gè)NetBIOS程序的工作過(guò)程中:首先,網(wǎng)絡(luò)通信程序通過(guò)名字的標(biāo)識(shí)區(qū)別于其他程序,并將名字注冊(cè)到LANA中,獲得合法的網(wǎng)絡(luò)身份;4.2NetBIOS應(yīng)用服務(wù)與實(shí)現(xiàn)然后,網(wǎng)絡(luò)通信程序可以使用NetBIOS的數(shù)據(jù)報(bào)或會(huì)話服務(wù)與駐留在相同或不同主機(jī)中的其他應(yīng)用程序進(jìn)行通信。通信過(guò)程中,用戶(hù)還可以使用一般命令服務(wù)對(duì)適配器進(jìn)行管理和監(jiān)測(cè)。最后,在通信結(jié)束后,通信程序需要?jiǎng)h除已注冊(cè)的名字并釋放所占用的網(wǎng)絡(luò)資源。
1.名字服務(wù)
在1.3節(jié)中,曾討論過(guò)網(wǎng)絡(luò)中的應(yīng)用在建立通信前必須標(biāo)識(shí)自己,即進(jìn)行編號(hào)。NetBIOS采用名字來(lái)對(duì)一個(gè)進(jìn)程進(jìn)行編號(hào)。
每個(gè)NetBIOSLAN適配器在其所處的網(wǎng)絡(luò)中用一個(gè)或多個(gè)網(wǎng)絡(luò)名字來(lái)標(biāo)識(shí)自己,以區(qū)別于其他的適配器。網(wǎng)絡(luò)通信程序用名字開(kāi)始和結(jié)束會(huì)話,這樣就允許LAN應(yīng)用程序?qū)⑿畔⒄_地傳送到某個(gè)指定的適配器上的某個(gè)特定應(yīng)用程序上,并且還可以在發(fā)送信息中標(biāo)記發(fā)送該信息的來(lái)源。在多個(gè)程序配置一臺(tái)單獨(dú)的機(jī)器時(shí),每個(gè)程序都有獨(dú)特的NetBIOS名稱(chēng)。每臺(tái)支持應(yīng)用的PC通過(guò)用戶(hù)定義或內(nèi)部方法而獲得NetBIOS站名。一個(gè)NetBIOS名稱(chēng)包含16個(gè)字節(jié)(前15個(gè)字節(jié)由用戶(hù)指定,第16個(gè)字節(jié)通常為保留值)。名稱(chēng)中每個(gè)字符都是有效的,并且對(duì)大小寫(xiě)敏感。名字的第一個(gè)字符不能是二進(jìn)制的0或*
號(hào)。一個(gè)應(yīng)用程序可使用多個(gè)名字,多個(gè)應(yīng)用程序也可以使用同一個(gè)給定的名字。一臺(tái)使用NetBIOS的PC在網(wǎng)絡(luò)上能完全工作起來(lái)之前,PC必須先登記NetBIOS名稱(chēng),即獲得注冊(cè)使用該名字的權(quán)利。登記NetBIOS名稱(chēng)的過(guò)程如下:
(1)客戶(hù)端A在所有地方廣播它自己和它的NetBIOS名字聲明包6到10次,確保其他網(wǎng)絡(luò)成員收到信息。
(2)如果有客戶(hù)端B已用聲明包中的NetBIOS名,客戶(hù)端B則發(fā)布它自己的廣播,包括它正在使用的名字,以請(qǐng)求登錄的客戶(hù)端A停止登記企圖??蛻?hù)端A收到客戶(hù)端B的廣播后,重新選取名字并返回上一步。
(3)如果無(wú)其他客戶(hù)端反對(duì)登記,請(qǐng)求登錄的客戶(hù)端A完成NetBIOS名稱(chēng)登記過(guò)程。
一個(gè)名字在成功注冊(cè)后,除了第一個(gè)注冊(cè)名字以外,以后均可用NetBIOS的名字管理命令來(lái)撤銷(xiāo)。重新啟動(dòng)系統(tǒng)或關(guān)閉工作站電源將會(huì)擦掉NetBIOS的命名表(見(jiàn)后面介紹)。NetBIOS名字根據(jù)功能的不同可以分為唯一名、組名、永久節(jié)點(diǎn)名、符號(hào)名。
(1)唯一名。唯一名是標(biāo)識(shí)與網(wǎng)絡(luò)上的單個(gè)用戶(hù)或計(jì)算機(jī)相關(guān)聯(lián)的某個(gè)資源的唯一名稱(chēng),通常用來(lái)向計(jì)算機(jī)上的特定進(jìn)程進(jìn)行網(wǎng)絡(luò)通信。如果一個(gè)應(yīng)用程序?qū)⒁粋€(gè)名字注冊(cè)為唯一名,那么在同一網(wǎng)絡(luò)上的其他應(yīng)用程序就不能再用這個(gè)名字注冊(cè)。如果這個(gè)唯一名又被其他應(yīng)用程序作為一個(gè)唯一名或組名在注冊(cè),那么使用該唯一名的適配器會(huì)發(fā)出沖突信號(hào),致使正在進(jìn)行的注冊(cè)工作遭到拒絕。如果一個(gè)應(yīng)用程序注冊(cè)一個(gè)唯一名成功,那么它在該網(wǎng)上擁有該名字的獨(dú)占權(quán)。
(2)組名。組名是標(biāo)識(shí)與網(wǎng)絡(luò)上的一組用戶(hù)或計(jì)算機(jī)相關(guān)聯(lián)的某個(gè)資源的名稱(chēng),通常用來(lái)同時(shí)向多臺(tái)計(jì)算機(jī)發(fā)送信息。如果一個(gè)應(yīng)用程序試圖將一個(gè)名字注冊(cè)成一個(gè)組名,那么這個(gè)名字必須是未被其他應(yīng)用程序注冊(cè)為唯一名字的,否則注冊(cè)就會(huì)失敗。如果這個(gè)名字已被注冊(cè)成為唯一名,那么必定會(huì)有一個(gè)應(yīng)用程序發(fā)出沖突信號(hào),使得要進(jìn)行的注冊(cè)遭到拒絕。否則,該應(yīng)用程序可以在網(wǎng)上非獨(dú)占地使用這個(gè)名字??梢栽试S其他應(yīng)用程序用同樣的名字作為組名重新注冊(cè),但不能作為唯一名來(lái)注冊(cè)。在將信息同時(shí)送往一組工作站時(shí),可利用組名進(jìn)行信息傳送。
(3)永久節(jié)點(diǎn)名。每塊IBMLAN適配器都有與它們對(duì)應(yīng)的一個(gè)唯一的6字節(jié)編號(hào),這個(gè)編號(hào)固化在適配器的ROM中,保證每塊IBMLAN適配器都能與其他的適配器區(qū)別開(kāi),這個(gè)編號(hào)稱(chēng)為永久節(jié)點(diǎn)名。永久節(jié)點(diǎn)名作為一個(gè)適配器發(fā)送的所有信息的LAN地址標(biāo)記,可作為所有發(fā)送信息的目的地址。這個(gè)48位的值構(gòu)成了決定哪些信息要被丟棄、哪些信息要被接收的電子信息過(guò)濾器。
(4)符號(hào)名。使用永久節(jié)點(diǎn)名來(lái)傳送信息容易發(fā)生錯(cuò)誤,因?yàn)橛谰霉?jié)點(diǎn)名的數(shù)字太多;同時(shí)當(dāng)工作站更換適配器時(shí),原先的程序就需要重寫(xiě),這些都造成了不必要的麻煩。因此可以采用相對(duì)較短的、與該工作站有關(guān)的自然名字將LAN適配器地址進(jìn)行擬人化標(biāo)記。這種假名稱(chēng)為符號(hào)名,并且它可以作為一個(gè)唯一名或組名在NetBIOS命名表中注冊(cè)。符號(hào)名可以被注冊(cè)和重新注冊(cè)。
(5)命名表及命名編號(hào)。在名字注冊(cè)成功后,適配器的NetBIOS將這個(gè)名字放到一個(gè)本地管理的內(nèi)部表中,即所謂的NetBIOS命名表(nametable),再將名字與一個(gè)字節(jié)的值一起向LAN應(yīng)用報(bào)告,注冊(cè)取得成功。這一個(gè)字節(jié)的值是一個(gè)無(wú)符號(hào)整數(shù),稱(chēng)為名字編號(hào)(namenumber),這個(gè)編號(hào)將被用于各種與該名字有關(guān)的NetBIOS命令中。NetBIOS以255為模,循環(huán)遞增地分配名字編號(hào),0和255為保留值,因此名字編號(hào)的順序?yàn)?,2,3,…,254,2,3,…,254,依次類(lèi)推。NetBIOS命名表是RAM中一個(gè)暫時(shí)性的表格,每次系統(tǒng)重啟或適配器復(fù)位后,這個(gè)命名表都要重構(gòu)。因?yàn)槊總€(gè)適配器都有自己專(zhuān)用的命名表,所以LAN上的NetBIOS的命名標(biāo)識(shí)問(wèn)題完全是自動(dòng)解決的,不需要命名管理中心。如果一個(gè)NetBIOS模塊支持一個(gè)工作站上的多個(gè)LAN適配器,那么適配器有自己獨(dú)立的NetBIOS命名表。在Windows操作系統(tǒng)下該命名表可以使用“nbtstat”命令進(jìn)行查詢(xún)。
名字服務(wù)分別使用AddName、AddGroupName、DeleteName三個(gè)命令在程序中實(shí)現(xiàn)相關(guān)功能,命令的含義詳見(jiàn)附錄2。
2.?dāng)?shù)據(jù)報(bào)服務(wù)
數(shù)據(jù)報(bào)是一種短信息,它的大小可由NetBIOS的執(zhí)行程序改變且不保證信息交付,即數(shù)據(jù)報(bào)不管信息是否安全到達(dá),NetBIOS也不提供接收數(shù)據(jù)。數(shù)據(jù)報(bào)可以將數(shù)據(jù)發(fā)送到特定的地點(diǎn)或組中所有的成員,也可廣播到整個(gè)局域網(wǎng)。數(shù)據(jù)報(bào)通信有廣播式數(shù)據(jù)報(bào)和普通數(shù)據(jù)報(bào)兩種。與其他數(shù)據(jù)服務(wù)相比,NetBIOS數(shù)據(jù)報(bào)是無(wú)連接、非可靠的一種通信方式。
1)廣播式數(shù)據(jù)報(bào)(廣播型)
廣播式數(shù)據(jù)報(bào)是用NetBIOS的發(fā)送廣播數(shù)據(jù)報(bào)命令所發(fā)送的。任何適配器(包括發(fā)送適配器),如果事前發(fā)送過(guò)NetBIOS接收廣播數(shù)據(jù)報(bào)命令,它就可以接收到這個(gè)廣播數(shù)據(jù)報(bào)。廣播數(shù)據(jù)報(bào)可能引起進(jìn)程間數(shù)據(jù)收發(fā)的混亂,應(yīng)當(dāng)謹(jǐn)慎使用。
2)普通數(shù)據(jù)報(bào)(定向型)
普通數(shù)據(jù)報(bào)是用NetBIOS的發(fā)送數(shù)據(jù)報(bào)命令發(fā)送的。與NetBIOS發(fā)送廣播數(shù)據(jù)報(bào)命令不同的是,應(yīng)用程序要用發(fā)送數(shù)據(jù)報(bào)名指出接收的NetBIOS的名字。普通數(shù)據(jù)報(bào)可被傳送給使用唯一名的一個(gè)適配器,或傳送給共享同一組名的一組適配器,包括發(fā)送適配器在內(nèi)的任何適配器,都可接收到數(shù)據(jù)報(bào)。
數(shù)據(jù)報(bào)通信的主要優(yōu)點(diǎn)是它所消耗的工作資源比會(huì)話通信少,省去了建立連接所需的開(kāi)銷(xiāo)。但這種類(lèi)型的服務(wù)不提供任何保障。除非依賴(lài)自服務(wù)器傳來(lái)的響應(yīng),否則會(huì)因目標(biāo)接收機(jī)退出網(wǎng)絡(luò)、掉電、數(shù)據(jù)丟失等多種原因?qū)е聰?shù)據(jù)傳輸失敗,不會(huì)提供任何錯(cuò)誤提示。數(shù)據(jù)報(bào)服務(wù)分別使用SendDatagram、ReceiveDatagram、SendBroadcast、ReceiveBroadcast等四個(gè)命令來(lái)在程序中實(shí)現(xiàn)相關(guān)功能,命令的含義詳見(jiàn)附錄2。
3.會(huì)話服務(wù)
NetBIOS會(huì)話連接是在兩個(gè)應(yīng)用程序之間建立一個(gè)可靠的虛電路,應(yīng)用程序可以駐留在同一個(gè)工作站(本地會(huì)話)或不同的工作站(遠(yuǎn)程會(huì)話)中,每一個(gè)應(yīng)用程序構(gòu)成會(huì)話或一邊。實(shí)際上,NetBIOS會(huì)話服務(wù)提供給用戶(hù)程序一種面向連接、可靠的、完全雙重的信息服務(wù)。NetBIOS會(huì)話的建立需要雙方預(yù)定的合作,會(huì)話過(guò)程需要一個(gè)是客戶(hù)端程序,一個(gè)是服務(wù)器端程序。
會(huì)話實(shí)際是一種雙向的通信數(shù)據(jù)流,通信的每一方都可向另一方發(fā)送消息。面向連接的服務(wù)可擔(dān)保在兩個(gè)端點(diǎn)之間,任何數(shù)據(jù)都能準(zhǔn)確無(wú)誤地傳遞。在這種服務(wù)中,服務(wù)器通常將自己注冊(cè)到一個(gè)已知的名字下。客戶(hù)機(jī)會(huì)搜尋這個(gè)名字,以便建立與服務(wù)器的通信。NetBIOS服務(wù)器進(jìn)程會(huì)針對(duì)想通過(guò)它建立通信的每一個(gè)LANA編號(hào),將自己的名字加入與其對(duì)應(yīng)的名字表。而對(duì)于其他機(jī)器上的客戶(hù)來(lái)說(shuō),就可將一個(gè)服務(wù)名解析成機(jī)器名,然后要求同服務(wù)器進(jìn)程建立連接。顯然,為建立這種虛擬回路,必須采取一些適當(dāng)?shù)牟襟E,通常會(huì)話過(guò)程包括建立會(huì)話、接收命令、發(fā)送命令、結(jié)束會(huì)話四個(gè)步驟。
1)建立會(huì)話
當(dāng)應(yīng)用程序發(fā)出NetBIOS監(jiān)聽(tīng)命令以訪問(wèn)NetBIOS名字表中的某個(gè)名字時(shí),會(huì)話就產(chǎn)生了。監(jiān)聽(tīng)命令也可指出一個(gè)遠(yuǎn)程名,申請(qǐng)對(duì)話的應(yīng)用程序把它看做會(huì)話伙伴。第二個(gè)應(yīng)用程序接著發(fā)出一條NetBIOS調(diào)用命令,去訪問(wèn)NetBIOS名字表中的名字,而這個(gè)名字正是第一個(gè)應(yīng)用所期望伙伴的名字。該調(diào)用命令還訪問(wèn)它在NetBIOS名字表中的第一個(gè)應(yīng)用程序的名字,兩個(gè)名字完全相匹配就會(huì)滿(mǎn)足兩個(gè)應(yīng)用程序建立會(huì)話的標(biāo)準(zhǔn),所進(jìn)行的監(jiān)聽(tīng)及調(diào)用命令也隨之完成。
2)接收命令
會(huì)話建立以后,在會(huì)話過(guò)程中雙方可以使用NetBIOS的send和receive命令操作傳輸數(shù)據(jù)。如果用一個(gè)名字產(chǎn)生幾個(gè)會(huì)話,則應(yīng)用程序可發(fā)出NetBIOS“指定數(shù)據(jù)任意接收”命令,該命令就提供與這個(gè)指定名有關(guān)的任何會(huì)話數(shù)據(jù)。更一般的情況是,應(yīng)用程序可以發(fā)出“任意名的任意接收”命令,提供適配器所建立的任何會(huì)話中的數(shù)據(jù)。
3)發(fā)送命令
應(yīng)用程序發(fā)出NetBIOSsend命令以把數(shù)據(jù)發(fā)送給其他應(yīng)用程序。該命令允許應(yīng)用程序發(fā)送的信息范圍是0~64
KB;數(shù)據(jù)必須處于連續(xù)的內(nèi)存中。應(yīng)用程序也可以通過(guò)NetBIOS鏈路發(fā)送命令,該命令允許數(shù)據(jù)駐留在兩個(gè)區(qū)域的緩沖區(qū)中。
4)結(jié)束會(huì)話
在會(huì)話最后,會(huì)話的一邊或兩邊發(fā)出NetBIOSHangup命令就可終止會(huì)話,各程序?qū)?zhí)行掛起命令。在以后發(fā)出這樣的命令時(shí),其他應(yīng)用程序也會(huì)得到會(huì)話結(jié)束的通知。應(yīng)用程序也可發(fā)出NetBIOS會(huì)話狀態(tài)命令以指出會(huì)話的狀態(tài)。
會(huì)話通信的優(yōu)點(diǎn)體現(xiàn)在能夠保證通信具有極高的可靠性,而且數(shù)據(jù)包的收發(fā)順序也能保證正確無(wú)誤。但是,會(huì)話是一種“以消息為基礎(chǔ)”的服務(wù),它的可靠性建立在建立會(huì)話、維護(hù)會(huì)話及適配器之間數(shù)據(jù)包應(yīng)答的開(kāi)銷(xiāo)上。需引起注意的是:NetBIOS假定局域網(wǎng)足夠快,能夠傳輸需要的數(shù)據(jù),因而沒(méi)有被會(huì)話服務(wù)的實(shí)際流控制。會(huì)話服務(wù)分別使用Call、Listen、Send、SendReceive、ReceiveAny、HangUp、ChainSend、SendNoAck、ChainSendNoAck、Sessionstatus等10個(gè)命令來(lái)在程序中實(shí)現(xiàn)相關(guān)功能,命令的含義詳見(jiàn)附錄2。
4.一般命令服務(wù)
一般命令可以對(duì)適配器進(jìn)行管理,包括復(fù)位、適配器的狀態(tài)顯示、查找名字、跟蹤、取消并斷開(kāi)鏈路等。通過(guò)這些命令,可以收集到大量有用的NetBIOS信息,從而了解網(wǎng)絡(luò)通信程序的運(yùn)行狀況,并有針對(duì)地管理適配器。
會(huì)話服務(wù)分別使用Reset、Astatus、FindName、Trace、Cancel、LanaEnum、Lanstatusalert、Unlink等8個(gè)命令來(lái)在程序中實(shí)現(xiàn)相關(guān)功能,命令的含義詳見(jiàn)附錄2。4.2.2NCB/MCB
前面所述的24個(gè)命令是實(shí)現(xiàn)四種服務(wù)的基礎(chǔ)。這些命令中的任何一個(gè)實(shí)現(xiàn)都要通過(guò)填寫(xiě)NCB/MCB,并將填寫(xiě)結(jié)果通過(guò)中斷提交給系統(tǒng)才能執(zhí)行,也就是說(shuō),應(yīng)用程序是通過(guò)使用網(wǎng)絡(luò)控制塊(NCB)實(shí)現(xiàn)對(duì)NetBIOS的訪問(wèn)的。NCB實(shí)際上是一個(gè)數(shù)據(jù)結(jié)構(gòu),在令牌環(huán)中也稱(chēng)為信息控制塊(MCB)。通常,應(yīng)用程序調(diào)用NetBIOS網(wǎng)絡(luò)服務(wù)功能時(shí),需要完成以下三個(gè)步驟:
(1)構(gòu)造一個(gè)NCB,這個(gè)NCB包含應(yīng)用程序想要進(jìn)行的NetBIOS操作描述;
(2)把NCB地址寫(xiě)入ES:BX寄存器,作為指向該NCB的遠(yuǎn)程地址指針;
(3)執(zhí)行5CH中斷,執(zhí)行時(shí),NetBIOS自動(dòng)進(jìn)入ES:BX寄存器指定的地址,讀取NCB得知一切有關(guān)該操作的信息。
Win32環(huán)境下,在VC++編譯器進(jìn)行編程過(guò)程中,除第一步以外,后面的兩步是通過(guò)調(diào)用Netbios()這個(gè)API函數(shù)一次性完成的。
1.NCB/MCB域結(jié)構(gòu)
NCB的結(jié)構(gòu)在nb30.h文件中定義。NCB有64個(gè)字符,分為14個(gè)域(或稱(chēng)為字段)和一個(gè)14字節(jié)的保留域。下面是NCB結(jié)構(gòu)的定義:
typedefstruct_NCB{
UCHAR ncb_command; /*命令碼*/
UCHAR ncb_retcode; /*返回碼*/
UCHAR ncb_lsn; /*本地會(huì)話編號(hào)*/
UCHAR ncb_num; /*名字編號(hào)*/
PUCHAR ncb_buffer; /*緩沖區(qū)地址*/
WORD ncb_length; /*緩沖區(qū)長(zhǎng)度*/
UCHAR ncb_callname[NCBNAMSZ]; /*調(diào)用(遠(yuǎn)程)名*/
UCHAR ncb_name[NCBNAMSZ]; /*本地名*/
UCHAR ncb_rto; /*接收超時(shí)*/
UCHAR ncb_sto; /*發(fā)送超時(shí)*/
void (CALLBACK*ncb_post)(struct_NCB*); /*POST例程地址*/
UCHAR ncb_lana_num; /*LANA編號(hào)*/
UCHAR ncb_cmd_cplt; /*命令結(jié)束標(biāo)志*/
UCHAR ncb_reserve[10]; /*保留*/
HANDLE ncb_event; /*Win32事件句柄*/
}NCB,*PNCB;
NCB各域的解釋如下:
1)命令域
每一個(gè)發(fā)往NetBIOS的NCB都代表一項(xiàng)要執(zhí)行的動(dòng)作,具體執(zhí)行哪項(xiàng)動(dòng)作,由命令字段的取值決定(參見(jiàn)附錄2)。NetBIOS命令的使用方式有兩種,即等待和非等待(或稱(chēng)為同步與異步),詳見(jiàn)“命令的完成”部分的介紹。
2)返回碼域
命令提交給NetBIOS驅(qū)動(dòng)程序后,該命令的成功與否即可在該字段中反映出來(lái)。若返回碼字段的值為00h,則表示命令成功。對(duì)于異步NetBIOS命令,NetBIOS將立即在返回碼字段中返回值FFh,表明該命令已經(jīng)排隊(duì),即將執(zhí)行。命令執(zhí)行完畢后,該字段將置成最終的返回碼。
3)本地會(huì)話編號(hào)域
同遠(yuǎn)程應(yīng)用程序處理建立了會(huì)話后,NetBIOS驅(qū)動(dòng)程序?qū)⑾鄳?yīng)地設(shè)置該字段(局部會(huì)話號(hào))。在隨后的通信中,若想同遠(yuǎn)程處理進(jìn)行通信,本地處理只需在NCB結(jié)構(gòu)中指明局部會(huì)話號(hào),不再需要在本地會(huì)話編號(hào)字段中指定完整的遠(yuǎn)程處理邏輯名。
單就一個(gè)適配器而言,工作站上的每一個(gè)處理一次至多能進(jìn)行254個(gè)會(huì)話,只要指定相關(guān)的局部會(huì)話號(hào),就能達(dá)到會(huì)話的目的。系統(tǒng)保留值為0和255,不將它們作為局部會(huì)話號(hào)使用。
4)名字編號(hào)域
工作站上的每一個(gè)處理最多可向名表中加進(jìn)254個(gè)邏輯名。成功地將一個(gè)名字加進(jìn)LANA的私有名表后,NetBIOS將置名字編號(hào)字段值為該名在名表中的索引值(索引值稱(chēng)為名號(hào)),在以后同遠(yuǎn)程處理進(jìn)行的非連接式通信中,可使用這個(gè)名號(hào)。名號(hào)0和255為系統(tǒng)保留,而適配器物理地址總在名表中的第1項(xiàng)。
5)緩沖區(qū)地址域
該字段的值是要發(fā)送的數(shù)據(jù)緩沖區(qū)的地址,或者要在其中存放接收到的數(shù)據(jù)的緩沖區(qū)的地址。
6)緩沖區(qū)長(zhǎng)度
該字段指定的是由緩沖區(qū)地址字段指定的緩沖區(qū)的長(zhǎng)度。接收到一塊數(shù)據(jù)時(shí),NetBIOS將相應(yīng)地設(shè)置該字段。
7)調(diào)用(遠(yuǎn)程)名域
這是一個(gè)由應(yīng)用程序設(shè)置的16字節(jié)域,其值是遠(yuǎn)程處理的邏輯名。應(yīng)用程序設(shè)置一個(gè)連接或向遠(yuǎn)程處理發(fā)送一個(gè)數(shù)據(jù)表包時(shí),將相應(yīng)設(shè)置該字段。在遠(yuǎn)程驅(qū)動(dòng)程序連接正期待著接收連接呼叫的本地處理時(shí),NetBIOS將填寫(xiě)該字段。因此,接收呼叫的處理能夠找出遠(yuǎn)程呼叫方的名。第一個(gè)字節(jié)為“*”,代表任意遠(yuǎn)程名。8)本地名域
這是由應(yīng)用程序設(shè)置的16字節(jié)字段(所有的字節(jié)均有用),其值是本地處理的邏輯名,應(yīng)用程序設(shè)置一個(gè)連接或向遠(yuǎn)程處理發(fā)送一個(gè)數(shù)據(jù)表包時(shí),將相應(yīng)設(shè)置該字段。該字段的第一個(gè)字節(jié)不能是二進(jìn)制的0或
*
號(hào);另外,IBM保留了頭三個(gè)字節(jié),所以頭三個(gè)字節(jié)不能是IBM;最后,第16個(gè)字節(jié)不能是00h到1Fh之間的值。在局域網(wǎng)管理器環(huán)境下,最后一個(gè)字符(即第16個(gè)字節(jié))有特殊的含義。
9)接收超時(shí)域
當(dāng)期望從一個(gè)或數(shù)個(gè)遠(yuǎn)程處理接收到一個(gè)數(shù)據(jù)表包時(shí),應(yīng)用程序可在接收超時(shí)(接收時(shí)間限制)字段中指定等待的最大時(shí)間數(shù)值(以1/2秒為間隔單位)。若超過(guò)了指定時(shí)間仍未接收到包,則NetBIOS驅(qū)動(dòng)程序?qū)⒃诮邮粘瑫r(shí)字段中返回錯(cuò)誤。若接收超時(shí)字段值為00h,則表示阻止執(zhí)行,直到本地處理接收到一個(gè)數(shù)據(jù)包。
10)發(fā)送超時(shí)域
發(fā)送超時(shí)(發(fā)送時(shí)間限制)字段類(lèi)似于接收超時(shí)字段,但它指定的是等待NetBIOS發(fā)送命令(Send)完成的時(shí)間。若超過(guò)了指定時(shí)間,則將返回錯(cuò)誤。若發(fā)送超時(shí)字段值為00h,則表示不為發(fā)送操作指定時(shí)間限制。此時(shí),命令將阻止執(zhí)行,直到要么成功地發(fā)送了一個(gè)數(shù)據(jù)包,要么NetBIOS層停止了重試。
11)
POST例程地址域
在提交異步命令時(shí),應(yīng)用程序可以設(shè)置該字段。在MS-DOS中,應(yīng)用程序?qū)⒑筇幚砝痰牡刂诽钤谠撟侄沃小K^后處理例程,即命令執(zhí)行完畢后NetBIOS驅(qū)動(dòng)程序?qū)⒁{(diào)用的例程。
12)
LANA編號(hào)域
一臺(tái)工作站上可能有不止一個(gè)LAN卡或網(wǎng)絡(luò)協(xié)議(或傳輸驅(qū)動(dòng)程序),所以NCB中的LANA編號(hào)字段指明了應(yīng)用程序想使用哪一個(gè)網(wǎng)絡(luò)適配器上的哪個(gè)網(wǎng)絡(luò)協(xié)議。該字段稱(chēng)為L(zhǎng)AN適配器號(hào)或LANA號(hào),LANA編號(hào)在0~9之間。
在像MicrosoftLANManager這樣的網(wǎng)絡(luò)軟件環(huán)境中,可以同時(shí)裝入多個(gè)傳輸驅(qū)動(dòng)程序(例如TCP/IP、NetBIOS或XNS),其中每一個(gè)驅(qū)動(dòng)程序都提供了一個(gè)NetBIOS接口。同時(shí)一個(gè)工作站可能有不止一個(gè)LAN適配器卡,這種情況下,LANA編號(hào)字段指定的是某一特定對(duì),即應(yīng)用程序想使用的傳輸驅(qū)動(dòng)程序和LAN卡組合。例如:某工作站安裝了兩塊網(wǎng)卡,以及三種具有NetBIOS能力的傳輸協(xié)議,如NetBT/NBT(TCP/IP)、Nbf(NetBEUI)和NwlnkNb(IPX/SPX),那么該工作站就有6個(gè)LANA編號(hào)可能,如以下對(duì)應(yīng)關(guān)系所示:TCP/IP—網(wǎng)卡1,NetBEUI—網(wǎng)卡1,IPX/SPX—網(wǎng)卡1;TCP/IP—網(wǎng)卡2,NetBEUI—網(wǎng)
卡2,IPX/SPX—網(wǎng)卡2。這6個(gè)組合系統(tǒng)會(huì)賦予6個(gè)不同的LANA編號(hào)。
需說(shuō)明的是,LANA編號(hào)并不是按順序累加的,而是由系統(tǒng)隨機(jī)分配的。
注意:只有基于相同NetBIOS協(xié)議的程序才能相互通信。例如:服務(wù)器上的NetBIOS應(yīng)用程序A工作在LANA編號(hào)2上,在服務(wù)器上LANA編號(hào)2正好對(duì)應(yīng)于TCP/IP協(xié)議。假設(shè)客戶(hù)機(jī)上的某應(yīng)用程序B想要與A建立連接,恰好B也工作在LANA編號(hào)2上,然而客戶(hù)機(jī)上LANA編號(hào)2正好對(duì)應(yīng)于NetBEUI協(xié)議,這就會(huì)導(dǎo)致問(wèn)題:盡管兩者安裝的TCP/IP和NetBEUI都提供NetBIOS功能,但是協(xié)議不同導(dǎo)致了兩個(gè)應(yīng)用程序之間無(wú)法通信。因而在程序設(shè)計(jì)時(shí),為了保證一臺(tái)PC上的NetBIOS應(yīng)用程序具有一定的魯棒性,一方面要安裝盡可能多的提供NetBIOS服務(wù)的協(xié)議(最好是TCP/IP、NetBEUI、IPX/SPX三種都安裝);另一方面應(yīng)用程序應(yīng)當(dāng)對(duì)機(jī)器中每塊網(wǎng)絡(luò)適配器上的每種提供NetBIOS服務(wù)的協(xié)議,即每個(gè)LANA編號(hào)嘗試通信?;诖?,在設(shè)計(jì)程序時(shí),服務(wù)器應(yīng)用程序應(yīng)對(duì)每個(gè)LANA編號(hào)上的客戶(hù)機(jī)連接進(jìn)行監(jiān)聽(tīng);客戶(hù)機(jī)上的應(yīng)用程序需要通過(guò)本機(jī)安裝的每個(gè)LANA編號(hào)嘗試連接。
13)命令結(jié)束標(biāo)志域
NetBIOS驅(qū)動(dòng)程序利用該字段來(lái)表明異步命令已完成。起先,當(dāng)應(yīng)用程序提交一條異步命令時(shí),NetBIOS將置該字段值為FFh;待命令執(zhí)行完畢后,再將最終值填入該字段。也就是說(shuō),提交了一條非等待命令后,應(yīng)用程序可以監(jiān)視(輪詢(xún))該字段的取值以了解命令是否完畢,直到其值不再是FFh為止。
14)保留域
NCB的保留域?yàn)?4字符長(zhǎng)的保留域,NetBIOS可能用它來(lái)返回?cái)U(kuò)充的錯(cuò)誤信息。另外,NetBIOS在處理請(qǐng)求的過(guò)程中,用它來(lái)作暫存區(qū)。應(yīng)用程序不應(yīng)使用NCB的保留域,因?yàn)橐坏┧獾狡茐?,NetBIOS的行為將是不可預(yù)測(cè)的。
15)事件句柄域
在提交異步命令時(shí),應(yīng)用程序可以設(shè)置該字段。該域用于存放后處理例程的句柄。
注意:當(dāng)命令域的設(shè)置使用ASYNCH(異步)標(biāo)志時(shí),該域的設(shè)置與POST例程地址域的設(shè)置互斥,即其中必須有一個(gè)域的設(shè)置為0,而另一個(gè)為非0值。
2.命令的調(diào)用
填充N(xiāo)CB后,就可以調(diào)用Netbios()函數(shù)進(jìn)行NetBIOS命令的調(diào)用了。Netbios()函數(shù)是將ES:BX寄存器對(duì)指向NCB地址,調(diào)用INT5Ch中斷請(qǐng)求來(lái)實(shí)現(xiàn)命令的調(diào)用的,另一個(gè)調(diào)用NetBIOS請(qǐng)求的方法是使用INT2Ah請(qǐng)求。后一種方法將AX寄存器值賦為0400h或0401h,同樣,ES:BX寄存器對(duì)應(yīng)作為Ncb的遠(yuǎn)程地址指針。用INT2Ah調(diào)用NetBIOS中斷時(shí),若AX寄存器值賦為0400h,則表明在命令失敗時(shí)不會(huì)自動(dòng)重試所調(diào)用的命令。若AX寄存器值賦為0401h,則表明命令失敗后會(huì)重試該命令。調(diào)用INT5Ch中斷請(qǐng)求方法的程序代碼如下所示:
#defineNetbiosInt5c ((unsignedchar)0x5C)
#defineNetbiosInt21FunctionCode ((unsignedchar)0x2A)
voidNetbios(structNCB*NcbPtrNear)
{
unionREGSInRegs,OutRegs;
structNCBfar*NcbPtrFar =(structNCBfar*)NcbPtrNear;
segread(&SegRegs);
SegRegs.es=FP_SEG(NcbPtrFar);
InRegs.x.bx=FP_OFF(NcbPtrFar);
int86x(NetbiosInt5c,&InRegs,&OutRegs,&SegRegs);
}
通過(guò)執(zhí)行5Ch中斷,NetBIOS自動(dòng)進(jìn)入ES:BX寄存器指定的地址,讀取NCB并得知一切有關(guān)該操作的信息,最后通知硬件完成該操作。
3.命令的完成
當(dāng)一個(gè)應(yīng)用程序向NetBIOS提交NCB后,NetBIOS給發(fā)出請(qǐng)求的應(yīng)用程序提供了一個(gè)返回碼,它通過(guò)對(duì)返回碼的觀察就可以了解NetBIOS命令的完成情況。提供返回碼的方式取決于命令使用等待(同步)方式或是不等待(異步)方式選項(xiàng),即由命令域的高位為二進(jìn)制1或0來(lái)確定,可以通過(guò)在命令域中命令與ASYNCH標(biāo)志進(jìn)行“或”操作來(lái)實(shí)現(xiàn)。ASYNCH的值實(shí)際上為80h,稱(chēng)為高比特設(shè)置符(highbitset)。
1)等待選項(xiàng)
如果命令域的高位為二進(jìn)制數(shù)0,則命令使用等待方式。如果命令使用等待選項(xiàng),適配器直到完成命令后,才將控制權(quán)還給應(yīng)用程序。當(dāng)命令執(zhí)行完后,最后的返回碼放在AL寄存器,返回碼域及命令結(jié)束標(biāo)志域(Ncb返回碼的含義見(jiàn)附錄2NetBIOS命令返回值參考)??刂茩?quán)返回緊隨在NetBIOS請(qǐng)求中斷后的那條指令。
2)不等待選項(xiàng)
如果命令域的高位為二進(jìn)制數(shù)1,則命令使用不等待方式。如果命令使用不等待選項(xiàng),NetBIOS提交兩個(gè)返回碼。初始掃描NCB后,立即將返回碼放在AL寄存器,控制權(quán)返回緊跟在NetBIOS請(qǐng)求后面的那條指令。如果立即返回碼不是00h,則適配器不能成功執(zhí)行該命令。如果立即返回碼是FFh,適配器請(qǐng)求排入隊(duì)列,等待最終完成。當(dāng)適配器完成命令后,提供最后的返回碼。不等待選項(xiàng)時(shí)有POST域?yàn)?和不為0兩種情況,下面分別進(jìn)行討論:
(1)有POST的不等待選項(xiàng)。如果POST域不是0,NetBIOS在AL寄存器和返回碼域中都放置了最后返回碼。然后,NetBIOS保存寄存器的內(nèi)容,將ES:BX寄存器對(duì)指向已完成的NCB,將標(biāo)志壓入堆棧,屏蔽可屏蔽的中斷并執(zhí)行對(duì)POST例程的遠(yuǎn)程調(diào)用。
在應(yīng)用的POST例程中,最后的返回碼可以從AL寄存器或返回碼域中獲得,POST例程應(yīng)使用IRET指令返回到NetBIOS。POST例程不必保存或恢復(fù)寄存器內(nèi)容,POST例程中能調(diào)用NetBIOS請(qǐng)求。然而,因?yàn)镻CDOS不可重入,而某個(gè)DOS調(diào)用請(qǐng)求可能已被激活的POST例程所中斷,所以在POST例程中不能調(diào)用PCDOS請(qǐng)求。
(2)無(wú)POST的不等待選項(xiàng)。如果POST域是0,表明沒(méi)定義POST例程。最后返回碼放在命令結(jié)束標(biāo)志域及返回碼域。應(yīng)用程序絕不應(yīng)以循環(huán)查詢(xún)返回碼域來(lái)理解命令是否完成,但應(yīng)監(jiān)視命令結(jié)束標(biāo)志域的值,以了解什么時(shí)候0FFh發(fā)生改變。
NetBIOS常用的命令域操作代碼如表4-1所示(包括同步和異步方式)。表4-1NetBIOS命令操作代碼表4.2.3NetBIOS編程基礎(chǔ)
在了解了NCB的結(jié)構(gòu)和NetBIOS命令的執(zhí)行過(guò)程后,就可以進(jìn)行基于NetBIOS的網(wǎng)絡(luò)編程了。在NetBIOS程序開(kāi)發(fā)時(shí),一般都需要對(duì)適配器進(jìn)行初始化、復(fù)位、添加應(yīng)用程序名,在程序完成后還需要進(jìn)行適配器資源的釋放,這些命令包括LanaEnum、ResetAll、Addname、Cancel、Hangup、DelName等,它們都是進(jìn)行NetBIOS編程的基礎(chǔ),下面逐一介紹有關(guān)這些命令的程序代碼。
1.探測(cè)LANA資源
LanaEnum是幾乎所有NetBIOS應(yīng)用都會(huì)用到的一個(gè)基本函數(shù)。我們知道,一臺(tái)主機(jī)中可能不止安裝一塊網(wǎng)卡、一種具有NetBIOS功能的通信協(xié)議,為了了解可以使用的LANA資源,通過(guò)調(diào)用該函數(shù),可探測(cè)系統(tǒng)上可用的所有LANA編號(hào)。在填寫(xiě)NCB時(shí),首先對(duì)該NCB結(jié)構(gòu)清零,以防止內(nèi)存中原來(lái)的數(shù)據(jù)導(dǎo)致當(dāng)NetBIOS命令結(jié)束時(shí)NetBIOS錯(cuò)誤地進(jìn)入其他內(nèi)存區(qū)域。然后,為這個(gè)清零后的NCB塊填寫(xiě)參數(shù),在命令域填入NCBENUM命令索引,同時(shí)為返回值指定接收緩沖區(qū)(格式為L(zhǎng)ANA_ENUM,LANA_ENUM如下文所示)并設(shè)置緩沖區(qū)長(zhǎng)度,這樣就完成了LanaEnum命令的NCB塊的填寫(xiě)。最后我們只要調(diào)用NetBIOS函數(shù)(詳見(jiàn)4.2.2節(jié))來(lái)執(zhí)行該命令即可。如果該命令調(diào)用成功,將返回NRC_GOODRET值;如果返回值不為NRC_GOODRET,函數(shù)將返回錯(cuò)誤代碼,對(duì)錯(cuò)誤代碼的解釋見(jiàn)附錄2。
intLanaEnum(LANA_ENUM*lenum)
{
NCBncb;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command =NCBENUM;
ncb.ncb_buffer =(PUCHAR)(lenum);
ncb.ncb_length =sizeof(LANA_ENUM);
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("NetbiosNCBENUMERROR:%d\n",ncb.ncb_retcode);
returnncb.ncb_retcode;
}
returnNRC_GOODRET;
}
LANA_ENUM的格式:
Typedefstruct_LANA_ENUM
{
UCHARlength;
UCHARlana[MAX_LANA];
}LANA_ENUM;
并不是在對(duì)NetBIOS的每次調(diào)用中都需要用到NCB結(jié)構(gòu)內(nèi)的全部成員,因而在調(diào)用一個(gè)NetBIOS命令時(shí)并不一定要填寫(xiě)每一個(gè)NCB域;此外,NCB中的一些域具有輸出參數(shù)的功能,命令執(zhí)行后的返回值將填充到這些域中,詳見(jiàn)附錄2中的NetBIOS參考命令。還應(yīng)注意的是,在填寫(xiě)NCB結(jié)構(gòu)成員之前,必須對(duì)這個(gè)NCB結(jié)構(gòu)清零,以消除殘留參數(shù)的影響,當(dāng)然這也是一個(gè)良好的編程習(xí)慣。
2.復(fù)位LANA
ResetAll是幾乎所有NetBIOS應(yīng)用都會(huì)用到的一個(gè)基本函數(shù)。一旦擁有一個(gè)LANA-ENUM結(jié)構(gòu),并有來(lái)自LanaEnum的LANA編號(hào),就需要對(duì)每個(gè)LANA編號(hào)進(jìn)行復(fù)位,一方面這是一個(gè)好的編程習(xí)慣,另一方面有些操作系統(tǒng),如WindowsNT,要求使用前必須對(duì)每一個(gè)LANA號(hào)進(jìn)行復(fù)位。復(fù)位的方法很簡(jiǎn)單,在命令域填入NCBRESET命令索引、本地會(huì)話編號(hào)置1、依次輸入LANA號(hào)即可,所有的資源都會(huì)釋放。當(dāng)本地會(huì)話編號(hào)為0時(shí),資源會(huì)釋放且會(huì)分配新的資源,新資源的分配按照ncb_callname域的設(shè)置進(jìn)行分配。ncb.ncb_callname[0]指定同時(shí)進(jìn)行的最大會(huì)話數(shù)量;ncb.ncb_callname[1]指定每個(gè)LANA增加的最大NetBIOS名字?jǐn)?shù)量;ncb.ncb_callname[2]為T(mén)RUE時(shí),一個(gè)客戶(hù)機(jī)可以將機(jī)器名作為自己的NetBIOS進(jìn)程名。
intResetAll(LANA_ENUM*lenum,UCHARucMaxSession,UCHARunMaxName,BOOLbFirstname)
{
NCBncb;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command =NCBRESET;
ncb.ncb_callname[0] =ucMaxSession;
ncb.ncb_callname[1] =unMaxName;
ncb.ncb_callname[2] =(UCHAR)bFirstname;
for(inti=0;i<lenum->length;i++)
{
ncb.ncb_lana_num =lenum->lana[i];
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("NetbiosNCBRESET[%d]ERROR:%d\n",ncb.ncb_lana_num,ncb.ncb_retcode);
returnncb.ncb_retcode;
}
}
returnNRC_GOODRET;
}
3.添加程序名
名字是應(yīng)用程序標(biāo)識(shí)自己的重要參數(shù),只有把名字成功加入到名字表中,應(yīng)用程序才具有合法的網(wǎng)絡(luò)身份;同時(shí)通過(guò)名字區(qū)分,網(wǎng)絡(luò)通信程序才能夠與想要與之建立聯(lián)系的其他程序建立會(huì)話,或把數(shù)據(jù)報(bào)發(fā)送到指定的地方。添加程序名的命令很簡(jiǎn)單,在命令域填入NCBADDNAME命令索引,在LANA編號(hào)域填入LANA號(hào),在本地名域填寫(xiě)程序名。命令完成后會(huì)在ncb.ncb_num中返回名字編號(hào),名字編號(hào)是網(wǎng)絡(luò)信息傳遞命令所必需的參數(shù),十分重要。
intAddName(intlana,char*name,UCHAR*num)
{
NCBncb;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command =NCBADDNAME;
ncb.ncb_lana_num =lana;
strncpy((char*)ncb.ncb_name,name,strlen(name));
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("NetbiosNCBADDNAME[lana=%d;name=%s]ERROR:%d\n",lana,name,ncb.ncb_retcode);
returnncb.ncb_retcode;
}
*num =ncb.ncb_num;
returnNRC_GOODRET;
}
4.獲取適配器狀態(tài)
該程序命令用于取得本地或遠(yuǎn)程(由ncb_callname指向的應(yīng)用程序所運(yùn)行的位置決定)適配器的狀態(tài),在命令完成后將適配器狀態(tài)信息填充到一個(gè)ADAPTER_STATUS結(jié)構(gòu)中(該結(jié)構(gòu)的介紹略)。
intAstatus(ADAPTER_STATUS*astat,intlana,char*name)
{
NCBncb;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command =NCBASTAT;
ncb.ncb_buffer =(PUCHAR)astat;
ncb.ncb_length =sizeof(ADAPTER_STATUS);
memset(&ncb.ncb_callname,'',NCBNAMSZ);
strncpy((char*)&ncb.ncb_callname,name,strlen(name));
ncb.ncb_lana_num =lana;
if(Netbios(&ncb)!=NRC_GOODRET)
{
Printf("NetbiosNCBASTATERROR:%d\n",ncb.ncb_retcode);
returnncb.ncb_retcode;
}
returnNRC_GOODRET;
}
除了上述基本的NetBIOS命令外,AddGroupName、LanStalert等命令也非常有用,它們的程序設(shè)計(jì)方法類(lèi)似,讀者可以根據(jù)附錄2中的說(shuō)明自行設(shè)計(jì)。常用的NetBIOS命令可以集中定義在NetBIOScmd.h文件中,以方便調(diào)用。
為了更好地說(shuō)明NetBIOS的編程,設(shè)計(jì)一個(gè)用NetBIOS編寫(xiě)獲取網(wǎng)絡(luò)適配器信息的完整程序(對(duì)于程序中使用的已介紹過(guò)的函數(shù)不另行說(shuō)明,后同)。該程序利用網(wǎng)絡(luò)基本輸入/輸出系統(tǒng)NetBIOS創(chuàng)建了一個(gè)能獲取主機(jī)MAC(網(wǎng)絡(luò)適配器)信息及其他一些信息的文件Astatus.exe。
#include"stdafx.h"
#include<windows.h>
#include<stdio.h>
#include<nb30.h>
typedefstruct_ASTAT
{
ADAPTER_STATUSadapt;
NAME_BUFFERNameBuffer[30];
}ASTAT,*PASTAT;
ASTATAdapter;
intAstatus(ADAPTER_STATUS*astat,intlana,char*name){…}//略
intmain(intargc,char*argv[])
{
NCBncb;
UCHARuRetCode=0;
charname[16];
//resetadapter
ZeroMemory(&ncb,sizeof(NCB));
memset(&ncb,0,sizeof(ncb));
ncb.ncb_command =NCBRESET;
ncb.ncb_lana_num =0;
Netbios(&ncb);
memset(&ncb,0,sizeof(ncb));
//getthestatusoftheadapter
strcpy(name,"*");
if(uRetCode==Astatus((struct_ADAPTER_STATUS*)&Adapter,0,name))
printf("TheEthernetNumberis:%02x_%02x_%02x_%02x_%02x_%02x\n",
Adapter.adapt.adapter_address[0],
Adapter.adapt.adapter_address[1],
Adapter.adapt.adapter_address[2],
Adapter.adapt.adapter_address[3],
Adapter.adapt.adapter_address[4],
Adapter.adapt.adapter_address[5]);
if(Adapter.adapt.adapter_type==0xFF)
printf("TheadapterisTokenRingadapter.\n");
elseif(Adapter.adapt.adapter_type==0xFE)
printf("TheadapterisEthernetadapter.\n");
printf("Thesoftware-releaselevelis%d.%d\n",Adapter.adapt.rev_major,Adapter.adapt.rev_minor);
printf("Thenumberofnamesinthelocalnamestableis%d\n",A_count);
for(inti=0;i<A_count;i++)
printf("%s\n",Adapter.NameBuffer[i].name);
return0;
}首先程序構(gòu)造一個(gè)NCB結(jié)構(gòu),并對(duì)其進(jìn)行清0操作(為了簡(jiǎn)單起見(jiàn)僅對(duì)LANA0進(jìn)行處理)。由于本程序的功能是獲取適配器信息,沒(méi)有特指某一個(gè)應(yīng)用程序,因而ncb_callname域的第一個(gè)字符設(shè)為星號(hào),表示任意程序。接著程序執(zhí)行Astatus()函數(shù),執(zhí)行后的結(jié)果放入一個(gè)ASTAT結(jié)構(gòu)。ASTAT包含兩個(gè)成員,一個(gè)成員是ADAPTER_STATUS結(jié)構(gòu)的adapt,用于收集適配器的狀態(tài)信息;另一個(gè)成員為NAME_BUFFER結(jié)構(gòu)的NameBuffer指針,用于收集適配器上該LANA號(hào)0所屬的名字表。adapt的成員具有不同的含義:adapter_address指針指向包含適配器物理地址的緩沖區(qū);adapter_type的值代表適配器類(lèi)型,當(dāng)值為0xFF時(shí),適配器為令牌環(huán)網(wǎng)絡(luò)適配器;當(dāng)值為0xFE時(shí),適配器為以太網(wǎng)適配器;rev_major和rev_minor表示適配器版本號(hào);name_count包含適配器LANA0上的名字表中名字的個(gè)數(shù),當(dāng)然這些名字都被收集進(jìn)NameBuffer所指向的緩沖區(qū)。最后上述參量被打印出來(lái),執(zhí)行結(jié)果如圖4-3所示。圖4-3Astatus.exe的執(zhí)行結(jié)果為了方便上述NetBIOS基本函數(shù)復(fù)用,將常用的函數(shù)寫(xiě)入頭文件NetBIOScmd.h,使用語(yǔ)句
#include“NetBIOScmd.h”即可引用。此外,進(jìn)行NetBIOS程序的編寫(xiě)過(guò)程中還需引入靜態(tài)鏈接庫(kù)Netapi32.lib。方法是:在VC6.0編譯器的“project→setting→link”的“Object/librarymodules”選項(xiàng)里添加該靜態(tài)鏈接庫(kù),當(dāng)然可以在程序代碼中直接添加,參閱9.2.3節(jié)。
當(dāng)一個(gè)適配器在網(wǎng)絡(luò)中激活后,宿主機(jī)上的應(yīng)用程序?qū)⒆约旱拿肿?cè)到LANA編號(hào)上的名字表中,應(yīng)用程序就可以用NetBIOS與駐留在同一個(gè)或不同計(jì)算機(jī)上的其他應(yīng)用通信。應(yīng)用程序之間的網(wǎng)絡(luò)通信可以使用數(shù)據(jù)報(bào)或會(huì)話兩種方式。4.3數(shù)據(jù)報(bào)通信程序設(shè)計(jì)數(shù)據(jù)報(bào)是一條短信息,它的長(zhǎng)度隨NetBIOS實(shí)現(xiàn)方法的不同而不同。數(shù)據(jù)報(bào)不能保證數(shù)據(jù)的正確傳輸,也不提供來(lái)自接收方的指示,在很多情況下,如目標(biāo)不存在、未加電或不接收數(shù)據(jù)報(bào)時(shí),發(fā)出的數(shù)據(jù)報(bào)就可能不會(huì)被任何計(jì)算機(jī)接收到,但是數(shù)據(jù)報(bào)具有占用網(wǎng)絡(luò)資源少的優(yōu)點(diǎn),適合于發(fā)送重要性要求不高的一般信息。4.3.1數(shù)據(jù)報(bào)通信模型
廣播型和定向型數(shù)據(jù)報(bào)是數(shù)據(jù)報(bào)通信的兩種不同類(lèi)型,前者不區(qū)分接收者的身份,后者必須指定接收者的身份,這兩種信息傳遞方式實(shí)現(xiàn)的關(guān)鍵是對(duì)NetBIOS數(shù)據(jù)報(bào)發(fā)送和接收命令的調(diào)用。
廣播型數(shù)據(jù)報(bào)完全不區(qū)分接收者,可以使用NetBIOS的SendBroadcastDatagram命令來(lái)發(fā)送數(shù)據(jù),接收方可通過(guò)調(diào)用NetBIOS的ReceiveBroadcasetDatagram命令來(lái)接收數(shù)據(jù)。SendBroadcastDatagram命令給本地網(wǎng)上的每個(gè)NetBIOS系統(tǒng)發(fā)送信息。當(dāng)NetBIOS節(jié)點(diǎn)收到廣播數(shù)據(jù)后,執(zhí)行ReceiveBroadcastDatagram命令的每個(gè)進(jìn)程都收到數(shù)據(jù)。當(dāng)廣播數(shù)據(jù)被收到時(shí),或沒(méi)有ReceiveBroadcastDatagram命令在運(yùn)行,數(shù)據(jù)將被丟棄。
定向型數(shù)據(jù)報(bào)指定數(shù)據(jù)報(bào)接收者的組名,任何一方都可以調(diào)用NetBIOSSendDatagram命令發(fā)送數(shù)據(jù),接收方可以通過(guò)調(diào)用NetBIOS的ReceiveDatagram命令來(lái)接收發(fā)送過(guò)來(lái)的數(shù)據(jù)。Send_Datagram命令需要調(diào)用者設(shè)定目的名。如果目的名是組名,組中每個(gè)成員都將收到數(shù)據(jù)。Receive_Datagram命令的調(diào)用者必須確定它接收數(shù)據(jù)的本地名。除了實(shí)際數(shù)據(jù)外,Receive_Datagram也返回發(fā)送者的名稱(chēng)。如果NetBIOS收到數(shù)據(jù),但卻沒(méi)有Receive_Datagram命令在等待,數(shù)據(jù)將被丟棄。
同一個(gè)數(shù)據(jù)報(bào)通信程序一般可分為發(fā)送部分和接收部分,下面按照兩種不同類(lèi)型分別進(jìn)行程序介紹。4.3.2廣播型數(shù)據(jù)報(bào)程序
廣播型數(shù)據(jù)報(bào)程序datagramBC.exe的main首先用LanaEnum收集宿主機(jī)中所有可用的LANA編號(hào),然后用ResetAll重設(shè)每個(gè)LANA,這幾乎是所有NetBIOS應(yīng)用程序都要采用的兩個(gè)步驟。接著main為變量分配資源,這樣初始化工作就基本完成了。
完成初始化工作后,需要將程序注冊(cè)到LANA中。這里我們希望編寫(xiě)一個(gè)既能發(fā)送消息又能接收消息的程序,即LAN中收發(fā)端程序名相同。為了不產(chǎn)生名字沖突,因此采用NCBADDGRNAME命令的方式注冊(cè)名字。NCBADDGRNAME命令與NCBADDNAME命令的實(shí)現(xiàn)函數(shù)類(lèi)似,只不過(guò)是將命令域設(shè)為NCBADDGRNAME索引,其他設(shè)置均相同。
注意:在本程序中,數(shù)據(jù)報(bào)的接收不受名字的限制,只要執(zhí)行ReceiveBroadcastDatagram命令的程序都可以接收到本廣播式數(shù)據(jù)報(bào)程序發(fā)送的消息。
程序利用NCBADDGRNAME命令得到了名字編號(hào)dwNum,就可以進(jìn)行數(shù)據(jù)報(bào)的發(fā)送了,發(fā)送功能由DatagramSendBC()函數(shù)完成。函數(shù)在NCB命令域填入NCBDGSENDBC命令索引,在名字編號(hào)域填入編號(hào),在緩沖區(qū)地址域設(shè)置緩沖區(qū)buffer指針地址(用來(lái)存放需要發(fā)送的消息),在緩沖區(qū)長(zhǎng)度域設(shè)置緩沖區(qū)的長(zhǎng)度buflen,在LANA編號(hào)域填入LANA號(hào)lana,最后執(zhí)行Netbios()函數(shù)即可。如果函數(shù)執(zhí)行成功,buffer中的消息將通過(guò)LANA傳遞到LAN上,并會(huì)返回整數(shù)NRC_GOODRET。
當(dāng)main發(fā)出NCBDGSENDBC命令后,程序執(zhí)行廣播式數(shù)據(jù)報(bào)接收命令,接收LAN中由NCBDGSENDBC命令發(fā)送的任意消息。廣播式數(shù)據(jù)報(bào)接收命令在NCB命令域填入NCBDGRECVBC索引(這里我們將為每個(gè)lana投放一個(gè)接收命令,因此應(yīng)設(shè)置ASYNCH使接收成為異步命令),名字編號(hào)域填入編號(hào),緩沖區(qū)地址域設(shè)置緩沖區(qū)buffer(用來(lái)存放收到的消息),緩沖區(qū)長(zhǎng)度域設(shè)置緩沖區(qū)的長(zhǎng)度buflen,LANA編號(hào)域填入LANA號(hào)lana。這里我們還需再填入一個(gè)事件句柄hEvent,這個(gè)句柄是用來(lái)進(jìn)行接收監(jiān)控的,稍后將作解釋。最后執(zhí)行Netbios()函數(shù)。如果函數(shù)調(diào)用成功,會(huì)返回整數(shù)NRC_GOODRET。在為每個(gè)lana投放一個(gè)異步接收命令后,我們并不知道其中哪一個(gè)會(huì)首先接收到數(shù)據(jù)報(bào),因此采用一個(gè)監(jiān)控方法來(lái)完成等待任務(wù)。這個(gè)監(jiān)控方法采用Win32事件作為傳信機(jī)制,具體是利用處于循環(huán)狀態(tài)的WaitForMultipleObjects()函數(shù)監(jiān)控上述NCBLISTEN命令綁定的事件句柄來(lái)完成的。WaitForMultipleObjects()函數(shù)的第一個(gè)參數(shù)是監(jiān)控對(duì)象的個(gè)數(shù)。第二個(gè)參數(shù)是指向監(jiān)控對(duì)象句柄數(shù)組的指針,這里是上述lenum.length個(gè)NCBLISTEN命令的NCB事件域填入的事件hEvent[i]所處的數(shù)組指針hEvent。第三個(gè)參數(shù)是等待標(biāo)志位,如果為T(mén)RUE,表示所有事件都有響應(yīng)才有返回值;如果為FALSE,表示任何一個(gè)事件有響應(yīng)就有返回值。第四個(gè)參數(shù)為等待時(shí)間,設(shè)為INFINITE。通過(guò)WaitForMultipleObjects()函數(shù)的監(jiān)控,一旦任何一個(gè)NCBDGSENDBC接收到數(shù)據(jù)報(bào),WaitForMultipleObjects()函數(shù)將會(huì)返回這個(gè)NCBDGSENDBC的序號(hào)。接下來(lái),我們根據(jù)序號(hào)查看這個(gè)NCBDGSENDBC的NCB命令結(jié)束標(biāo)志域,如果不為NRC_PENDING接收成功,buffer中將填充從LAN上接收到的數(shù)據(jù)報(bào)。
最后main打印所收聽(tīng)到的消息,以及發(fā)送者的NetBIOS名。對(duì)于某些程序的NetBIOS名不能打印,可使用FormatNetbiosName()函數(shù)進(jìn)行轉(zhuǎn)化,這樣就完成了一個(gè)簡(jiǎn)單的廣播式數(shù)據(jù)報(bào)程序設(shè)計(jì),如下所示:
#include"stdafx.h"
#include<windows.h>
#include<stdio.h>
#include<nb30.h>
#include"NetBIOScmd.h"
#defineMAX_SESSIONS 254
#defineMAX_NAMES 254
#defineMAX_DATAGRAM_SIZE 512
//mainfunction
intmain(intargc,char*argv[])
{
LANA_ENUM lenum;
char szSender[NCBNAMSZ+1];
char szLocalName[NCBNAMSZ+1] ="datagram";
char *MessageArray =NULL,
num ='';
int i=0,
j=0,
len;
DWORD dwErr;
UCHAR *dwNum =NULL;
NCB *ncb =NULL;
HANDLE *hEvent =NULL;
DWORD dwRet;
if(LanaEnum(&lenum)!=NRC_GOODRET)
return1;
if(ResetAll(&lenum,(UCHAR)MAX_SESSIONS,(UCHAR)MAX_NAMES,false)!
=NRC_GOODRET)
return1;
ncb =(NCB*) GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,
sizeof(NCB)*lenum.length);
dwNum =(UCHAR*) GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,
sizeof(UCHAR)*lenum.length);
MessageArray =(char*) GlobalAlloc(GMEM_FIXED,MAX_DATAGRAM_SIZE);
hEvent =(HANDLE*) GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,
sizeof(HANDLE)*lenum.length);
for(i=0;i<lenum.length;i++)
{
dwErr=AddGroupName((int)lenum.lana[i],szLocalName,&dwNum[i]);
printf("addgroupname(%s)tolana[%d]\n",szLocalName,lenum.lana[i]);
}
strcpy(MessageArray,"testbroadcastmassage");
for(i=0;i<lenum.length;i++)
{
len=strlen(MessageArray);
DatagramSendBC(lenum.lana[i],dwNum[i],MessageArray,len);
}
for(j=0;j<lenum.length;j++)
{
hEvent[j]=CreateEvent(0,TRUE,F(xiàn)ALSE,0);
if(DatagramRecvBC(&ncb[j],lenum.lana[j],dwNum[j],MessageArray,
MAX_DATAGRAM_SIZE,hEvent[j])!=NRC_GOODRET)
return1;
}
printf("setuphasbeenfinished,nowbeginstolisten.\n\n\n");
dwRet=WaitForMultipleObjects(lenum.length,hEvent,F(xiàn)ALSE,INFINITE);
if(dwRet==WAIT_FAILED)
{
printf("WaitForMultipleObjectsfailed:%d\n",::GetLastError());
return1;
}
for(j=0;j<lenum.length;j++)
{
if(ncb[j].ncb_cmd_cplt==NRC_PENDING)
Cancel(&ncb[j]);
else
{
ncb[j].ncb_buffer[ncb[j].ncb_length]=0;
FormatNetbiosName((char*)ncb[j].ncb_callname,szSender);
printf("message:[%s],\ncomesfrom[%s],\nreceivedfrom:[LANA%d].\n",
MessageArray,szSender,ncb[j].ncb_lana_num);
ResetEvent(hEvent[j]);
}
}
for(i=0;i<lenum.length;i++)
{
DelName(lenum.lana[i],szLocalName);
CloseHandle(hEvent[i]);
}
GlobalFree(MessageArray);
GlobalFree(hEvent);
GlobalFree(ncb);
return0;
}
程序編譯完以后,可以將其復(fù)制到LAN上的多臺(tái)機(jī)器上并分別運(yùn)行。當(dāng)任意一臺(tái)機(jī)器處于接收等待狀態(tài)時(shí)再運(yùn)行一個(gè)datagramBC.exe程序,其他處于等待的程序都會(huì)打印消息“testbroadcastmassage”。4.3.3定向型數(shù)據(jù)報(bào)程序
定向型數(shù)據(jù)報(bào)程序把數(shù)據(jù)報(bào)發(fā)送到注冊(cè)為指定的唯一名或組名網(wǎng)絡(luò)應(yīng)用程序上。在程序編寫(xiě)上與廣播型數(shù)據(jù)報(bào)程序的區(qū)別在于:數(shù)據(jù)報(bào)發(fā)送和接收分別采用NCBDGSEND命令和NCBDGSRECV命令。這兩個(gè)命令的實(shí)現(xiàn)函數(shù)與DatagramSendBC()和DatagramRecvBC()非常相似,如下所示。
DatagramRecv()與DatagramRecvBC()的區(qū)別僅是命令索引不同,為NCBDGRECV;而DatagramSend()與DatagramSendBC()的區(qū)別除了命令索引不同,為NCBDGSEND外(參見(jiàn)附錄1),還必須在調(diào)用名域填寫(xiě)接收應(yīng)用程序的NetBIOS名(可以是唯一名,也可以是組名),數(shù)據(jù)報(bào)的接收將受到這個(gè)名字的限制。
intDatagramSend(intlana,intnum,char*buffer,intbuflen,char*name)
{
NCBncb;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command =NCBDGSEND;
ncb.ncb_lana_num =lana;
ncb.ncb_num =(UCHAR)num;
ncb.ncb_buffer =(PUCHAR)(buffer);
ncb.ncb_length =buflen;
memset(ncb.ncb_name,'',NCBNAMSZ);
strncpy((char*)ncb.ncb_name,name,strlen(name));
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("NetbiosNCBDGSENDBCERROR:%d\n",ncb.ncb_retcode);
returnncb.ncb_retcode;
}
returnNRC_GOODRET;
}
//receiveadirecteddatagramonthespecifiedLANAnumberfromtheregisteredname
intDatagramRecv(PNCBpncb,intlana,intnum,char*buffer,intbuflen,HANDLEhEvent)
{
ZeroMemory(pncb,sizeof(NCB));
ZeroMemory(buffer,sizeof(MAX_DATAGRAM_SIZE));
pncb->ncb_command =NCBDGRECV|ASYNCH;
pncb->ncb_lana_num =lana;
pncb->ncb_num =num;
pncb->ncb_buffer =(PUCHAR)(buffer);
pncb->ncb_length =buflen;
pncb->ncb_event =hEvent;
if(Netbios(pncb)!=NRC_GOODRET)
{
printf("NetbiosNCBDGRECVBCERROR:%d\n",pncb->ncb_retcode);
returnpncb->ncb_retcode;
}
returnNRC_GOODRET;
}用上述函數(shù)替換廣播型數(shù)據(jù)報(bào)程序datagramBC.exe中的DatagramRecv()與DatagramRecvBC(),并做相應(yīng)處理(為程序注冊(cè)不同的名字),就實(shí)現(xiàn)了定向型數(shù)據(jù)報(bào)程序的設(shè)計(jì)。在運(yùn)行多個(gè)這樣的程序時(shí)會(huì)發(fā)現(xiàn),只有與DatagramSend()設(shè)置的
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年山東省濱州市中考英語(yǔ)試題含解析
- 四年級(jí)心理健康教案
- 山東省青島市膠州市2024-2025學(xué)年七年級(jí)上學(xué)期 第一次月考英語(yǔ)試卷(無(wú)答案)
- 2013-2020年全球PET瓶坯模具行業(yè)市場(chǎng)深度調(diào)查及戰(zhàn)略投資分析研究報(bào)告
- 2024至2030年中國(guó)異型車(chē)數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 2010-2013年熱塑性彈性體市場(chǎng)運(yùn)行態(tài)勢(shì)及預(yù)測(cè)分析報(bào)告
- 2024至2030年中國(guó)帶玻璃夾板門(mén)行業(yè)投資前景及策略咨詢(xún)研究報(bào)告
- 2024至2030年中國(guó)寬幅門(mén)板生產(chǎn)線數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 2024至2030年中國(guó)合金鋁片數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 2024至2030年中國(guó)前排氣動(dòng)打磨機(jī)行業(yè)投資前景及策略咨詢(xún)研究報(bào)告
- 2025屆【九省聯(lián)考】全國(guó)高三10月聯(lián)考數(shù)學(xué)試題
- 第1-4單元測(cè)試卷(單元測(cè)試)-2024-2025學(xué)年四年級(jí)上冊(cè)數(shù)學(xué)人教版
- GB/T 44489-2024高級(jí)輔助駕駛地圖審查要求
- 2024-2030年中國(guó)氫能源行業(yè)發(fā)展趨勢(shì)與項(xiàng)目投資專(zhuān)項(xiàng)調(diào)研報(bào)告
- T-CECS120-2021套接緊定式鋼導(dǎo)管施工及驗(yàn)收規(guī)程
- 人教版八年級(jí)上冊(cè)數(shù)學(xué)期中考試試題含答案詳解
- 2023--2024學(xué)年蘇少版七上綜合實(shí)踐教案
- 《嬰幼兒常見(jiàn)病識(shí)別與預(yù)防》課件-嬰幼兒濕疹
- 坦克介紹英語(yǔ)解讀
- 大學(xué)體育理論(山東聯(lián)盟)智慧樹(shù)知到課后章節(jié)答案2023年下泰山學(xué)院
- 新聞寫(xiě)作培訓(xùn)主題課件
評(píng)論
0/150
提交評(píng)論