IPX Network Programing

參考自:Peter Quiring

資訊二 B85506057 蔡怡仁

我對文章作了一些增加與解釋,但會尊重原作者的原創性的。

 

IPX是一種通訊協定被發展出來應用在允許一台機器的應用程式和另一台透過網路溝通。這種通訊協定的使用最常被應用在網路遊戲上,諸如很有名的WAR2、doomlike game、DIABLO等,但是它被應用最多的就是在Novell的網路流通上。而也有一個跟它非常相近的通訊協定稱為SPX。而兩者之間的唯一不同點就是,當你要傳一堆資料封包到另外一台電腦而使用IPX通訊協定時,對方電腦收到封包的順序會跟你傳封包的順序一模一樣。但如果是SPX通訊協定的話,這點就不能擔保了,這點對即時性的資料(譬如即時性的遊戲資料)就會發生沒辦法處理判定的問題了。所以在這裡我們只討論關於IPX通訊協定的使用。

當我們要在網路上用IPX通訊協定溝通時通常有兩個名詞的關係是必要存在的,那就是server(talker)(或翻伺服器)與client(listener)(或翻客戶端)。但這是等一下要討論的,我們必須先從一些必須做的起使工作開始:

 

Initialization:

 

mov ax,7a00h

int 2fh

;然後傳回al值

.if al=0ffh

;表示IPX通訊協定已經安裝了

.else

;表示IPX通訊協定還未安裝

.endif

 

現在我們知道當我們要起使化軟體的IPX通訊協定是非常簡單的(當然先決條件必須要已經對硬體載入驅動程式)。然後我們可以開一個socket(socket就是一個網路facility(應該可以說是一種自由格式化的資料空間吧),能夠提供使用者「插入」軟體去提供網路更多的服務),然後我們可以從這個socket來存取我們要的東西。IPX網路使用64K大小的socket,但是第一個32K大小已經被用在網路的作業系統上,所以我們必須用到第二個free的32K。選擇socket是很重要的,最好能選擇一個沒有被其他常用軟體程式使用的socket,否則可能使用者將因為執行其他會共用到socket程式時而無法執行你的軟體。(就好像我們要連線打quake的時候要先設定好socket number一樣)

 

Open socket:

 

mov ax,0

mov bx,0

mov dx,desired_socket(希望的socket number)

int 7ah

;(傳回值)

;al=status(0=ok,ff=already open,fe=socket table full)

;dx=socket number given

 

一旦一個socket被打開之後,我們就可以開始使用它了。假如另一個程式也使用同樣的的socket的話,它將可以被允許去與你的程式做溝通。當我們要傳遞或接受封包的時候,為了去呼叫IPX的driver去得到關於哪裡該放data等等的資訊,有一些struct是需要的。這裡有兩類的struct是必備的,而我們必須每一類都有兩個struct,一個是用來關於傳送的,另一個是用來關於接收的。

 

Structures:(資料結構的儲存型態,至於每一項是用來幹麼的介紹在後面會有概述)

 

ECB struct

LinkAddressOff dw ? ;ignore

LinkAddressSeg dw ? ;ignore

ESRAddressOff dw ? ;leave 0

ESRAddressSeg dw ? ;leave 0

InUse db ?

CompCode db ?

SockNum dw ?

IPXWorkSpc dd ? ;leave 0

DrvWorkSpc db 12 dup (?) ;leave 0

ImmAdd db 6 dup (?)

FragCount dw ?

FragAddOfs dw ?

FragAddSeg dw ?

FragSize dw ?

ECB ends

 

IPX struct

Checksum dw ?

Length dw ?

Control db ?

PacketType db ?

DestNet dd ?

DestNode df ? ;6 bytes

DestSocket dw ?

SourNet dd ?

SourNode df ?

SourSock dw ?

Datagram db packet_size dup (?)

IPX ends

 

Struct values:

ECB.Linkaddressoff - 這個可以不用去理他。

ECB.Linkaddressseg - 這個可以不用去理他。

ECB.ESRaddressoff - 這個應該要是0 - it's the Event Service Request。

ECB.ESRaddressseg - 這個應該要是0。

ECB.InUse - 這個byte將會記錄由IPX的driver視是否一個資料封包已經被傳遞出去或已被接收到。

ECB.CompCode - 這個byte記錄最近一次動作的狀態(status of the operation)。

ECB.Socknum - 記錄我們要在其上溝通的socket number(必須是已經經過open的socket喔!)。

ECB.IPXWorkSpc - IPXWorkSpc記錄我們要透過其溝通的server編號,假如DRVWorkSpc已經被使用了0ffffffffffffh,那最靠近的server將會被使用。

ECB.DRVWorkSpc - 記錄IPX的driver工作所涵蓋的空間。

ECB.ImmAdd - 當我們收到一個加入的資料封包時,這裡將會被填入適當的訊息,也會拷貝一份到ECB struct的sent部份。

ECB.FragCount - 使用了幾個IPX struct(通常一個啦)。

ECB.FragAddoff - IPX struct的offset位址。

ECB.FragAddseg - IPX struct的segment位址。

ECB.FragSize - 整個IPX struct的大小(包括所有的資料)。

 

IPX.Checksum - 在使用IPX的driver之前,必須要先把這項設為零。

IPX.Length - 記錄IPX封包的長度(包括data)

IPX.Control - 這項要設為零 - 以後將會由IPX的driver來填。

IPX.PacketType - 設為零(unknown)

IPX.DestNet - 在起使化的時候,對於預設的網路設定是0。一旦收到了另外一台電腦傳來的封包時,這 個地方就會被填入封包的位址。

IPX.DestNode - 1.設為0ffffffffffffh的話,表示要把封包傳給所有的node(computer)。

2.相對假如從其他電腦收到一個封包,我們也可以把node(這台電腦)記錄在這裡。

IPX.DestSocket - 記錄我們要在其上溝通的socket number。

IPX.SourNet - 這是記錄收到的封包的來源(source)。

IPX.SourNode - 這是記錄收到的封包的來源node(source node)。

IPX.SourSock - 這是記錄收到的封包的socket used in received packet

IPX.Datagram - 這個封包大小的data buffer用來儲存你要送出還是要接收的封包資料。

 

整個IPX struct的header不能太大(就是到儲存資料的buffer前),不能超過1024 bytes。因為我們要留下足夠的空間來記錄封包的資料。

 

Sending data packets:

 

;先要已經設定好 ECB and IPX structs

mov bx,3

mov es,seg ECB

mov si,offset ECB

int 7ah

 

Receiving data packets:

 

;先要已經設定好 ECB and IPX structs

mov bx,4

mov es,seg ECB

mov si,offset ECB

int 7ah

 

Relinquish Control:

當我們要要傳遞或接收封包時,我們必須要給IPX driver時間去完成它必須做的事,所以我們必須透過Relinquish Control(釋放控制)把控制權轉移給IPX driver。

 

mov ax,0

mov bx,0ah

int 7ah

 

這個指令必須重複呼叫直到ECB struct的InUse的旗標有改變為止(意即封包被放出或收到)。而當此時,所有結構裡的元素將會由IPX的driver來填。InUse旗標將會被IPX driver設為0ffh假如我們使用傳遞或接收的指令。它將會一直保持這個狀態直到有錯誤發生還是指令動作完成。假如變成0表示指令動作成功的完成,其他的表示有錯誤發生。我們可以看CompCode去檢視看到底發生了什麼事情(錯誤)。

 

CompCodes:

這些CompCodes(compellation codes)將會在InUse改變時被同時設定。

而InUse最初會被IPX driver設定為零。

 

Send Compcodes:

00 sent

FC canceled

FD malformed packet

FE no listener (undelivered)

FF hardware failure

 

Receive CompCodes:

00 received

FC canceled

FD packet overflow

FF socket was closed

FE Listening(繼續試著接收)

 

Cancel Send/Receive:

當我們在傳遞或接收封包時,而想要去停止動作時,我們可以做Cancel Send/Receive的動作。

 

mov bx,6

mov es,seg ECB

mov si,offset ECB

int 7ah

;傳入的al=compcode (should be 0FCh = canceled)

 

IPX Disconnect:

一旦我們要離開我們的應用程式,我們將必須要停止兩台電腦(或更多)間的溝通。停止兩台電腦(或更多)間的溝通就必須告訴其他電腦我們不要繼續做溝通了。這需要第三個structure。

 

FullNetAddr struct

NetWork dd ?

Node df ?

Socket dw ?

FullNetAddr ends

 

以上的結構中的元素必須要適當地填入clients的address。

然後呼叫以下的動作:

 

mov bx,0bh

mov es,seg FullNetAddr

mov si,offset FullNetAddr

int 7ah

 

Close Socket:

 

當我們完成了一個socket的使用,我們必須close它。

 

mov bx,1

mov ax,0

mov dx,Socket#

int 7ah

 

以上就是我們要使用IPX通訊協定所必須做的前後準備工作程序。

 

備註:

當要設計一個網路遊戲時,我們必須要去選擇程式要在網路上溝通的方式。我們可以選擇使用最普遍被使用的client/server模式,或者維持使用一個通用的broadcasting方式(每一台每次對每一台連接的電腦都傳送資料,相對於小部份彼此溝通)(當然是使用一個socket),但這種方式將會在傳輸上非常地慢。

最好的方式是只使用一個socket,架構structs去使用這個socket並且設定為一個通用的廣播方式。廣播資料封包應該內含資料用來告訴其他台電腦「我在找program server」(而不用去煩惱哪一台才是server),假如網路上已經有一台server在跑程式(game)了,當它收到這個訊息的時候,必須要告訴這個新加入的遊戲者,而且這個新加入遊戲者也必須把模式調整為只對server送出訊息(由server統一發送訊息)。假如經過了一段時間我們還是沒有找到server,程式應該自動設定本身這一台為server,並且開始進行遊戲。

雖然「即時性」對於網路遊戲是非常重要的,但我們不能夠說能夠「完全」的同時開始執行遊戲行程。因為假設兩台電腦同時開始,表示說兩台電腦都必須設為server,想當然而這是不可能的,所以資料必須要由一個server來統籌發送,利用資料在網路上流通「極短暫」的延遲性,達到我們看到的「即時性」效果。

根據上點,網路的流通順暢性是非常重要的,這也是當網路上沒有server啟動,而有兩部以上的電腦在找尋server時,我們最好讓資料傳送較順暢的電腦作為server的原因了(要用程式作測試判定)。故假如有一個新的遊戲者擁有較佳的資料傳送速度加入遊戲時,遊戲的設計最好設計會把server搬移到新的遊戲者電腦上。但是這裡要注意的是,所謂的資料傳輸速度,包括了電腦本身的速度(畢竟處理最近的遊戲,都需要能夠吃重的硬體),以及網路的品質。所以不一定一台Pentium II就一定會比486快,但這不是我們所需顧及的,我們要注意的是資料傳遞的順暢,是總體的評估,而不是這些分項的考量。另外一件事必須注意的是,假如扮演server的那台機器突然離開或是crash掉了,那我們必須趕緊把server的權力任務移交給另一台(第二順位)的電腦,讓能遊戲能繼續進行。舊的遊戲通常S有做這方面的處理,所以只要server掛了,大家都跳出來了。舉個例子來說好了,假如你有玩過Descent,當server跳出的時候,你一定會收到這類廣播的訊息。