生活随笔
收集整理的這篇文章主要介紹了
uIP 一个免费的TCP/IP栈
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
uIP?一個免費的TCP/IP棧 原文:
Adam Dunkels adam@dunkels.com 2002年2月15日 翻譯:張偉林?? 2003年5月17日
okelinchang@163.com ? 摘要 這個文檔描述uIP TCP/IP棧。 uIP TCP/IP棧是使用于低至8位或16位微處理器的嵌入式系統的一個可實現的極小的TCP/IP協議棧。現時,uIP代碼的大小和RAM的需求比其它一般的TCP/IP棧要小。 uIP棧使用一個基于編程模塊事件去減少代碼的大小和RAM的使用量。基于系統的底層和uIP之間的接口的回應會在文檔里描述。系統的底層和uIP之間的接口是隱蔽的。文檔后面包含了一些uIP應用編程例子。 uIP 的代碼和這個文檔的新版本可以在uIP的主頁下載?http://dunkels.com/adam/uip/。 這個文檔描述了uIP的0.6版。 1?引言 新近這些年里,人們對連接一個甚至只是小裝置到一個現有的IP網絡例如全球因特網的興趣增加了。為了可以通過因特網通訊,一個可實現的TCP/IP協議棧是必須的。uIP是一個可實現的TCP/IP協議組件的一個非常重要的部分。uIP的實現目標是保持代碼大小和儲存器使用量最小。現時,uIP代碼的大小和RAM的需求比其它一般的TCP/IP棧要小。uIP使用C編程語言,它可以自用分發和使用于商業和非商業目的。 其它的TCP/IP棧,儲存器經常用于數據緩存,等待一個數據已經成功送達的確應信號。事實上,數據包丟失了,數據必須重發。有特色的是,數據是緩存在RAM里,如果需要重發數據,應用程序可以快速重生數據。例如,一個HTTP服務器服務的大部分是ROM里的靜態和半靜態頁,不需要在RAM里緩存靜態內容。所以,如果一個包丟失了,HTTP服務器可以容易地從ROM里重生數據。數據簡單地從原先的位置讀回來。uIP的優越性是允許應用程序參加數據重發。 這個文檔由以下部分組成,第2節描述在系統和應用的立場上怎樣使用uIP。第3節詳細討論協議實現細節。第4?節覆蓋了uIP的配置,第5節描述uIP的結構部分。最后,第6節提供一些uIP的應用編程實例。 2 ?uIP的接口技術 uIP可以看作是一個代碼庫為系統提供確定的函數。圖1?展示了uIP,系統底層和應用程序之間的關系。uIP提供三個函數到系統底層,?uip_init(),uip_input(),和uip_periodic()。應用程序必須提供一個回應函數給uIP。當網絡或定時事件發生時,調用回應函數。?uIP提供許多函數和堆棧交互。 要注意的就是uIP提供的大部分函數是作為C的宏命令實現的,主要是為了速度,代碼大小,效率和堆棧的使用。 圖 1?uIP就好像一個庫
2.1 uIP應用接口 BSD套節字接口使用于大部分的操作系統,它不適合微系統,因為在應用設計里,它逼使一個線程基于編程模塊。一個多線程環境代價重大,因為,不但在線程管理里涉及增加代碼的復雜性,而且保存每線程堆棧需要額外的儲存器,還有執行任務切換的時間開銷也攤派在這里。微型系統不會有足夠的資源去實現一個多線程環境,因此需要這個環境的應用接口不適合uIP。 相反,uIP使用一個基于編程模塊的事件,模塊是實現應用程序作為一個C函數被uIP調用的地方,uIP響應一定的事件。uIP調用應用在,當接收數據時,當數據成功送達另一方中止連接時,當一個新的連接建立時,或者當數據需要重發時。應用程序也周期性地循環等待新數據。應用程序只提供一個回應函數;它提升了應用程序處理不同的網絡服務的不同的端口和連接的映射 uIP與其它TCP/IP棧不同的是,當正在重發工作,它需要應用程序的幫助。其它TCP/IP棧緩存傳輸數據在儲存器里,直到在連接的最后數據確應成功發送。如果數據需要重傳,堆棧在沒有通知應用程序下監視著重傳工作。通過這種方法,當要等待一個確應,數據必須緩存在儲存器里,如果產生一個重發,應用程序可以快速重新生成數據。為了減少儲存器的使用量,uIP利用的論據是應用程序可以重新生成發送的數據和讓應用程序參加重發。 2.1.1 uIP應用事件
應用程序必須作為C函數去實現,uIP在任何一個事件發生時調用UIP_APPCALL()。表1?列出可能的事件和每個事件的對應測試函數。測試函數用于區別不同的事件。函數是作為C宏命令實現的,將會是零值或非零值。注意的是某些函數可以在互相連接時發生(也就是新數據可以在數據確應的同時到達)。 表?1: uIP應用事件和對應的測試參數 | 一個數據包到達,確應先前發送到數據 | uip_acked() |
| 應用程序的新數據包已經到達 | uip_newdata() |
| 一個遠程主機連接到監聽端口 | uip_connected() |
| 一個到達遠程主機的連接成功建立 | uip_connected() |
| 計時時間滿重發 | uip_rexmit() |
| 計時時間滿周期性輪詢 | uip_poll() |
| 遠程主機關閉連接 | uip_closed() |
| 遠程主機中斷連接 | uip_aborted() |
| 由于太多重傳,連接中斷 | uip_timedout() |
當應用程序調用時,uIP設置全局變量uip_conn去指向當前連接的uip_conn結構?(圖5)?。這可以用來區別不同的服務。一個典型的應用是檢查uip_conn->lport?(當地TCP端口號)去決定那個服務連接應該提供。例如,如果值uip_conn->lport等于80,應用程序可以決定啟動一個HTTP服務,值是23是啟動TELNET服務。 2.1.2 接收數據
如果uIP測試函數uip_newdata()值為1,遠程連接的主機有發送新數據。uip_appdata指針指向實際數據。數據的大小通過uIP函數uip_datalen()獲得。在數據不是被緩沖后,應用程序必須立刻啟動。 2.1.3 發送數據
應用程序通過使用uIP函數uip_send()發送數據。uip_send()函數采用兩個參數;一個指針指向發送數據和數據的長度。如果應用程序為了產生要發送的實際數據需要RAM空間,包緩存(通過uip_appdata指針指向)可以用于這方面。 在一個時間里應用程序只能在連接中發送一塊數據。因此不可以在每個應用程序啟用中調用uip_send()超過一次;只有上一次調用的數據將會發出后才可以。注意,調用uip_send()以后會改變某些全局變量,在應用函數返回前它不能被調用。 2.1.4 重發數據
如果數據在網絡中丟失,應用程序必須重發數據。無論數據收到或沒有收到,uIP保持跟蹤,和通知應用程序什么時候察覺出數據是丟失了。如果測試函數uip_rexmit()為真,應用程序要重發上一次發出的數據。重發就好像原來那樣發送,也就是通過uip_send()。 2.1.5 關閉連接
應用程序通過調用uip_close()關閉當前連接。這會導致連接干凈地關閉。為了指出致命的錯誤,應用程序可以中止連接和調用uip_abort()函數完成這個工作。 如果連接已經被遠端關閉,測試函數uip_closed()為真。應用程序接著可以做一些必要的清理工作。 2.1.6 報告錯誤
有兩個致命的錯誤可以發生在連接中,不是連接由遠程主機中止,就是連接多次重發上一數據和被中止。uIP通過調用函數報告這些問題。應用程序使用兩個測試函數uip_aborted()和uip _timedout()?去測試那些錯誤情況。 2.1.7 輪詢
當連接空閑時,uIP在每一個時候周期性地輪詢應用程序。應用程序使用測試函數uip_poll()去檢查它是否被輪詢過。 2.1.8 監聽端口 uIP維持一個監聽TCP端口列表。通過uip_listen()函數,一個新的監聽端口打開。當一個連接請求在一個監聽端口到達,uIP產生一個新的連接和調用應用程序函數。如果一個新連接產生,應用程序被調用,測試函數uip_connected()為真。 2.1.9 打開連接 作為uIP的0.6版,在uIP里面通過使用uip_connect()函數打開一個新連接。這個函數打開一個新連接到指定的IP地址和端口,返回一個新連接的指針到uip_conn結構。如果沒有空余的連接槽,函數返回空值。為了方便,函數uip_ipaddr()可以用于將IP地址打包進兩個單元16位數組里,通過uIP去代表IP地址。 使用兩個例子,在圖2?和圖3?展示。第一個例子展示了怎樣打開一個連接去遠端TCP端口8080。如果沒有足夠的TCP連接插槽去允許一個新連接打開,uip_connect()函數返回NULL和通過uip_abort()中止當前連接。第二個例子展示怎樣打開一個新連接去指定的IP地址。這例子里沒有錯誤檢查。 void connect_example1_app(void) { if(uip_connect(uip_conn->ripaddr, 8080) == NULL) { uip_abort(); } } 圖?2:打開一個連接去當前連接的遠端的端口8080 void connect_example2(void) { u16_t?ipaddr[2]; uip_ipaddr(ipaddr, 192,168,0,1); uip_connect(ipaddr, 8080); } 圖?3:?打開一個到主機192.168.0.1上端口8080的連接 2.1.10 數據流控制
通過函數uip_stop()和uip_restart(),uIP提供存取TCP數據流的控制途徑。設想一個應用程序下載數據到一個慢速設備,例如磁盤驅動器。如果磁盤驅動器的作業隊列滿了,應用程序不會準備從服務器接收更多的數據,直到隊列排出空位。函數uip_stop()可以用于維護流控制和停止遠程主機發送數據。當應用程序準備好接收更多數據,函數uip_restart()用于告知遠程終端再次發送數據。函數uip_stopped()可以用于檢查當前連接是否停止。 2.2 uIP/系統接口 從系統的立場看,?uIP由3個C函數uip_init(),uip_input(),?和uip_periodic()。uip_init()函數用于初始化uIP堆棧和在系統啟動期間調用。當網絡設備驅動器讀一個IP包到包緩存時,調用函數uip_input()。周期性運行是調用uip_periodic(),代表的是一秒一次。調用uIP函數是系統的職責。 2.2.1 uIP/設備驅動接口 當設備驅動放一個輸入包在包緩存里(uip_buf),系統應該調用uip_input()函數。函數將會處理這個包和需要時調用應用程序。當uip_input()返回,一個輸出包放在包緩存里。包的大小由全局變量uip_len約束。如果uip_len是0,沒有包要發送。 2.2.2 uIP/周期計時接口 周期計時是用于驅動所有uIP內部時鐘事件,例如包重發。當周期計時激發,每一個TCP連接應該調用uIP函數uip_periodic()。連接編號傳遞是作為自變量給uip_periodic()函數的。類似于uip_input()函數,當uip_periodic()函數返回,輸出的IP包要放在包緩存里。圖4?展示了調用uip_periodic()函數和監視輸出包的一小段代碼。在這個特別的例子,函數netdev_send()是網絡驅動的部分,將uip_buf數組的目錄發出到網上。 for(i = 0; i < UIP_CONNS; ++i) { uip_periodic(i); if(uip_len > 0) netdev_send(); } 圖?4:周期計時和uIP的接口的例子代碼. ? 2.3 uIP 函數總結 表2包含了所有uIP提供的函數 表?2: uIP?函數總結 | 系統接口 | |
| uip_init() uip_input() uip_periodic() | 初始化uIP 處理輸入包 處理周期計時事件 |
| 應用程序接口 | |
| uip_listen() uip_connect() uip_send() uip_datalen() uip_close() uip_abort() uip_stop() uip_stopped() uip_restart() | 開始監聽端口 連接到遠程主機 在當前連接發送數據 輸入數據的大小 關閉當前連接 中止當前連接 停止當前連接 查找連接是否停止 重新啟動當前連接 |
| 測試函數 | |
| uip_newdata() uip_acked() uip_connected() uip_closed() uip_aborted() uip_timeou() uip_rexmit uip_poll() | 遠程主機已經發出數據 確應發出的數據 當前連接剛連上 當前連接剛關閉 當前連接剛中止 當前連接剛超時 數據重發 應用程序循環運行 |
| 其它 | |
| uip_mss() uip_ipaddr() htons(),ntohs() | 獲得當前連接的最大的段大小 將IP地址結構打包 在主機和網絡之間轉換字節次序 |
? 3?實現協議 uIP實現了TCP/IP協議組的四個基本協議;?ARP [Plu82],IP [Pos81b], ICMP [Pos81a]?和?TCP [Pos81c]。鏈路層協議例如PPP可以實現作為uIP下面的設備驅動。應用層協議例如?HTTP, FTP或SMTP可以實現為uIP之上的應用程序。 3.1?地址解析協議?| ARP ARP協議映射了IP地址和以太網MAC物理地址,它在以太網上的TCP/IP操作是需要的。ARP在uIP里實現的是包含一個IP到MAC地址的映射。當一個IP包要在以太網上發出,查詢ARP表,去找出包要發送去的MAC地址。如果在表里找不到IP地址,ARP請求包就會發出。請求包在網絡里廣播和請求給出IP地址的MAC地址。主機通過發出一個ARP回應,響應請求IP地址。當uIP給出一個ARP回應,更新ARP表。 為了節省儲存器,一個IP地址的ARP請求覆蓋發出的請求輸出IP包。它是假定上層將重新發送那些被覆蓋了的數據。 每十秒表更新一次,舊的條目會被丟棄。默認的ARP表條目生存時間是20分鐘。 3.2?網際協議?| IP uIP的IP層代碼有兩個職責:驗證輸入包的IP頭的正確性和ICMP?和TCP協議之間多路復用。?IP層代碼是非常簡單的,由9條語句組成。事實上,uIP的IP層極大地簡化了,它沒有實現碎片和重組。 3.3?因特網信息控制協議?| ICMP 在uIP里,只有一種ICMP信息實現了:?ICMP回響信息。ICMP回響信息常常用于ping程序里的檢查主機是否在線。在uIP里,ICMP回響處理在一個非常簡單的方式。ICMP類型字段的改變是從?/echo"類型到?/echo reply"類型,從而?ICMP調整校驗和。其次,IP地址里的IP頭交換,包發回到原先的發送者。 3.4?傳輸控制協議| TCP 為了減少儲存器的使用,uIP里的TCP沒有實現發送和接收數據的調整窗口。輸入的TCP段不會通過uIP緩存,但必須立即由應用程序處理。注意這不能避免應用程序自己緩沖數據。輸出數據時,uIP不能在每個連接有超過一個未解決的TCP段。 3.4.1?連接狀態 在uIP,每個TCP連接的完全態包含當地和遠端的TCP端口編號,遠程主機的IP地址,重發時間值,上一段重發的編號,和連接的段的最大尺寸。除此之外,每個連接也可以保持一些應用狀態。三個序列號是,期望接收的下一個字節的序列號,上一發送段第一字節的序列號,下一發送字節的序列號。連接的狀態由uip_conn結構表現,可以在圖5看到。一個uip_conn結構數組用于在uIP里保持所有的連接。數組的大小等于同時的最大數量的連接,它在編譯時間里設置(看第4?節)。 struct uip_conn { u8_t?tcpstateflags; /* TCP狀態和標志. */ u16_t ?lport, rport; /*當地和遠端端口. */ u16_t ?ripaddr[2]; /*同等遠端的IP地址. */ u8_t ?rcv_nxt[4]; /*?我們期待接收的下一個序列號. */ u8_t ?snd_nxt[4]; /*上一個發送的序列號. */ u8_t?ack_nxt[4]; /*?通過從遠端的下一個應答去應答序列號. */ u8_t ?timer; /*?重發時間r. */ u8_t ?nrtx; /*計算特殊段的重發數量. */ u8_t ?mss; /*?連接的最大段大小. */ u8_t ?appstate[UIP_APPSTATE_SIZE]; }; 圖?5:?uip_conn?結構 3.4.2輸入處理 TCP輸入處理和檢驗TCP校驗和一起開始。如果校驗和是對的,在當前活動的TCP連接之間,源、目的端口號和IP地址復用包。沒有活動的連接符合輸入包時,如果包不是一個監聽端口的連接請求,包丟棄。如果包是一個關閉端口的請求,uIP發一個?RST包回應。 如果發現了一個監聽端口,uip_conn結構數組掃描任何一個非活動連接。如果發現一個,數組由新連接的端口號和IP地址填充。如果連接請求攜帶一個TCP MSS (最大段大小)選擇,它會分析,再次檢查當前最大段大小MSS去決定當前連接的MSS,前后者的最小值會被選擇。最后,一個回應包發去確應開啟連接。 應該將輸入包送去一個已經活躍的連接,包的序列號和從遠端主機來的期望的下一個序列號一起被檢查?(uip_conn結構里的rcv_nxt?變量顯示于圖5)。如果序列號不是期望得到的下一個,包會被丟掉和發一個ACK去指出期望得到的下一個序列號。緊接著,檢查輸入包里的確應號,看看是否確應連接的所有輸出數據。它做了后,應用程序會知道這個事實的。 當序列號和確應號被檢測過,依靠當前TCP狀態,包將會被不同地處理。如果連接在SYN-RCVD狀態和輸入包確應先前發送的SYNACK包,連接將會輸入ESTABLISHED狀態,調用應用函數去通知已經完全連接。在連接的建立狀態,如果有新數據由遠端主機發送或者遠端主機確應之前發送的數據,就調用應用函數。 當應用函數返回,TCP檢查應用程序是否還有數據要發。如果有,一個TCP/IP包會形成在包緩存里。 3.4.3?輸出處理 輸出處理過程比輸入處理直接和簡單得多。基本上,所有TCP和IP頭字段由uip_conn?結構里的值充滿,計算TCP和IP的校驗和。當uip_process()函數返回,包通過網絡設備驅動發出去。 3.4.4?重發 當uIP通過periodic_timer被調用時,重發就進入運作(看段2.2.2)。連接里有些特殊的數據(也就是數據發出了去但仍沒有確應的)通過UIP_OUTSTANDING位在uip_conn?結構里的TCP狀態標志變量標記(圖5)。那個連接,時間變量減少。當時間到達零,上一段必須重發和調用應用函數去做真正的重發。如果一個特殊段重發編號超出一個可設置的界限,連接會結束和發一個RST段到遠端連接結束,調用應用函數去通知它連接超時。 3.4.5?重置TCP TCP規格要求如果TCP頭里的序列號和確應號在當前連接的接收窗口失去了,有RST (復位)標志設置的包必須斷開連接。為了減少代碼的大小,uIP不嚴格遵守這個規定。相反,如果一個有RST標志設置的包在連接里到達,連接會消滅那些不重要的序列號和確應號值。這個行為將會在將來的uIP版本修訂。 4?配置?uIP uIP的設置隱藏在一個叫uipopt.h的單獨頭文件里。這個文檔不及包含了那些項目特性的設置選項?(例如uIP網點的IP地址和同時發生連接的最大值),而且有結構和C編譯器的特殊選項。文檔是獨立的和有注釋說明的。 5?構做具體函數 當?IP,ICMP和TCP協議在單一的C函數里實現(uip_process()函數),它們需要四個支持函數的幫助。支持函數實現32位添加和計算校驗和。在uIP的0.4版本,支持函數的實現由實際的協議拆分,這是為了易于手工將支持函數匯編。在支持函數頻繁被調用后,有實際的增益是使那些函數運行得越來越快 四個支持函數中有兩個是計算IP和TCP校驗和,uip_ ipchksum(),uip_tcpchksum(),另兩個是執行TCP序列號的32位添加:uip_add_ack_nxt()和uip_add_rcv_nxt()。uIP分類包括簡單的支持函數C實現。 uip_ipchksum()計算和返回網間IP頭的校驗和[BBP88],但沒有進行檢驗和的位非操作。IP頭可以在uip_buf數組的頭20字節找到。 uip_tcpchksum()函數計算TCP校驗和。TCP校驗和是TCP頭和數據的網間校驗。這個函數事實上有點復雜,TCP頭和TCP數據可以在不同的儲存器位置找到。在uip_buf數組開頭可以找到TCP頭的20字節(也就是在&uip_buf[20])?,uip_appdata指針指向TCP數據的開頭。TCP數據的大小可以通過從整個包中減去IP和TCP頭得出。包的大小包含在uip_len這個全局變量里。因為uIP不支持數據流里的IP或者TCP選擇,IP和TCP頭的大小是40字節。 6?應用例子 這節提供一些簡單的uIP應用例子 6.1?一個簡單的應用例子 第一個例子非常簡單。應用程序監聽輸入連接的端口1234。當一個連接建立了,應用程序通過說“OK”回應所有發送給它的數據。 圖6?顯示了應用程序的實現。應用程序調用example1_init()初始化,uIP的回叫函數是example1_app(),在這個例子里,可設置的變量UIP_APPCALL應該要定義在example1_app。 初始化函數調用uIP函數uip_listen()去注冊一個監聽端口。實際的應用函數example1_app()使用測試函數uip _newdata()和uip_rexmit()去確定為什么調用它。如果調用應用程序是因為最遠端發了數據給它,它回應一個"ok"。如果調用應用函數是因為數據在網絡里丟失和需要重發,它也發送一個?"ok"。 注意,這個例子顯示了一個完全的uIP應用。應用程序不需要處理所有類型事件例如:??????uip _connected()或uip_timedout()。 void example1_init(void) { uip_listen(1234); } void example1_app(void) { if(uip_newdata() || uip_rexmit()) { uip_send("ok/n", 3); } } 圖?6:?一個非常簡單的應用程序 6.2?一個更高級的應用 第二個例子只是稍微比第一個高級一點,顯示了程序中狀態段怎樣在uip_conn結構中使用。 這個應用程序和第一有些相似,它監聽一個輸入連接的端口和回應發送給它的數據一個"ok"。最大的不同是當連接建立時,這個程序打印輸出一個歡迎信息"Welcome!"。 程序怎樣實現,表面上是操作上的小改動產生效果上很大的不同。復雜性增加的原因是如果數據在網絡里丟失,程序必須知道那個數據需要重發。如果"Welcome!"信息丟失了,程序必須重發歡迎信息,如果其中一個"ok"信息丟失,程序必須發一個新的"ok"。 程序知道只要"Welcome!"信息沒有被遠程主機確應,它就可能在網絡里丟失了。但一旦遠程主機已發了一個確應回來,程序可以應為歡迎信息已經被接收,知道一些丟失的數據是一個"ok"信息。因此程序可以在兩個之中的一個狀態:在WELCOME-SENT狀態,"Welcome!"已經發出但沒有被確應,或者WELCOME-ACKED狀態,"Welcome!"已被確應。 當遠程主機連接到應用程序時,程序發一個"Welcome!"信息和發它的狀態去WELCOME-SENT。當歡迎信息被確應了,程序進入WELCOME-ACKED狀態。如果程序從遠程主機收到任何新數據,它回應一個"ok"?。 如果應用程序被請求重發上一條信息,它看程序在那個狀態。如果程序在WELCOME-SENT狀態,它知道先前的歡迎信息沒有被確應,它再發一個"Welcome!"信息。如果程序在WELCOME-ACKED狀態,它知道上一條信息是?"ok"?,只發一條信息。 應用的實現可以在圖7看到。圖8顯示了應用的設置 struct ?example2_state { enum {WELCOME_SENT, WELCOME_ACKED} state; }; void example2_init(void) { uip_listen(2345); } void example2_app(void) { struct?example2_state *s; s = (struct ?example2_state *)uip_conn->appstate; if(uip_connected()) { s->state = WELCOME_SENT; uip_send("Welcome!/n", 9); return; } if(uip_acked() && s->state == WELCOME_SENT) { s->state = WELCOME_ACKED; } if(uip_newdata()) { uip_send("ok/n", 3); } if(uip_rexmit()) { switch(s->state) { case WELCOME_SENT: uip_send("Welcome!/n", 9); break; case WELCOME_ACKED: uip_send("ok/n", 3); break; } } } 圖?7:?一個高級應用 #define UIP_APPCALL example2_app #define UIP_APPSTATE_SIZE sizeof(struct ?example2_state) 圖?8:?應用程序設置 6.3?區分應用程序 如果系統要運行多個應用程序,區分它們的其中一個技術是使用連接的遠程終端或當地終端的TCP端口編號。圖9?顯示了怎樣將上面的例子結合在一個應用程序里。 void example3_init(void) { example1_init(); example2_init(); } void example3_app(void) { switch(uip_conn->lport) { case htons(1234): example1_app(); break; case htons(2345): example2_app(); break; } } 圖?9:?兩個應用程序組合和使用不同的當地端口 6.4?接收大量數據 這個例子顯示了一個簡單的程序連接去主機,發一個文件的HTTP請求和從慢速設備如磁盤驅動器下載文件。這個顯示了怎樣使用uIP的流控制函數。程序顯示在圖10。 當連接已經建立,一個HTTP請求發去服務器。這是唯一的發送數據,應用程序知道是否需要重發數據,那么請求要重發。所以要合理的將兩個事件結合在例子里。 當應用程序從遠程主機接收數據,它通過使用函數device_enqueue()發這些數據去設備。要注意的是這個例子假設這個函數復制數據去它自己的緩存。在uip_appdata緩存的數據將會被下一個輸入數據覆蓋。 如果等待設備的隊列滿了,應用程序通過調用uIP函數uip_stop()停止來之遠程主機的數據。應用程序確保它不接收任何數據,直到調用uip_restart()。應用程序輪詢事件,用于檢查隊列是否不在滿,如果沒滿,通過uip_restart()數據流重新開始。 void example4_init(void) { u16_t?ipaddr[2]; uip_ipaddr(ipaddr, 192,168,0,1); uip_conn(ipaddr, 80); } void example4_app(void) { if(uip_connected() || uip_rexmit()) { uip_send("GET /file HTTP/1.0/r/nServer:192.186.0.1/r/n/r/n",48); return; } if(uip_newdata()) { device_enqueue(uip_appdata, uip_datalen()); if(device_queue_full()) { uip_stop(); } } if(uip_poll() && uip_stopped()) { if(!device_queue_full()) { uip_restart(); } } } 圖?10:?一個接收大量數據的應用程序 6.5?一個簡單的網絡服務器 這個例子顯示了一個非常簡單的文件服務器程序和使用端口編號去決定發送那個文檔。如果文檔適當格式化一下,這個簡單的程序可以作為一個有靜態網頁的網絡服務器。圖11?顯示了實現方法。 應用程序的狀態包括一個指向要發送數據的指針和剩下發送數據的大小。當一個遠程主機連接去應用程序,當地端口編號用于決定那個文檔要發送。第一塊數據使用uip_send()發送。由于有監管,至多的最大段字節數據發送了。 應用程序由肯定應答驅動。當數據被確應,新數據可以發送。如果沒有數據要發送了,連接使用uip_close()關閉。 struct example5_state { char ?*adaptor; unsigned ?int?dataleft; }; void example5_init(void) { uip_listen(80); uip_listen(81); } void example5_app(void) { struct ?example5_state *s; s = (struct ?example5_state)uip_conn->appstate; if(uip_connected()) { switch(uip_conn->lport) { case htons(80): s->dataptr = data_port_80; s->dataleft = datalen_port_80; break; case htons(81): s->dataptr = data_port_81; s->dataleft = datalen_port_81; break; } uip_send(s->dataptr , uip_mss() < s->dataleft? uip_mss(): s->dataleft); return; } if(uip_acked()) { if(s->dataleft < uip_mss()) { uip_close(); return; } s->dataptr += uip_mss(); s->dataleft -= uip_mss(); uip_send(s->dataptr , uip_mss() < s->dataleft? uip_mss(): s->dataleft); } } 圖?11:?一個簡單的文件服務器 參考 [BBP88] R. Braden, D. Borman, and C. Partridge. Computing the internet checksum. RFC 1071, Internet Engineering Task Force, September 1988. [Plu82] D. C. Plummer. An ethernet address resolution protocol. RFC 826,Internet Engineering Task Force, November 1982. [Pos81a] J. Postel. Internet control message protocol. RFC 792, Internet Engineering Task Force, September 1981. [Pos81b] J. Postel. Internet protocol. RFC 791, Internet Engineering Task Force, September 1981. [Pos81c] J. Postel. Transmission control protocol. RFC 793, Internet Engineering Task Force, September 1981. Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=752383
轉載:http://blog.csdn.net/ccsk2006/article/details/1422849
總結
以上是生活随笔為你收集整理的uIP 一个免费的TCP/IP栈的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。