社区导航

 

搜索
查看: 4031|回复: 2

lwip翻译的最好的一个

[复制链接]

72

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2010-12-22 16:47 | 显示全部楼层 |阅读模式

感觉这个不错,给大家分享一下

 

 

刚完成win32下的lwip移植 调试测试 程序。

使用winpcap做底层。

只要电脑 不需要额外 硬件就可以马上测试lwip.以后可能可以给大家汇报测试结果



---------------------------------------------------------------
Raw TCP/IP interface for lwIP 0.5

lwIP 0.5 中的原始 TCP/IP 介面

 

 

作者:Adam Dunkels,2001/12/12

譯者:ct小貓 (Elisha Chou周錦廷 ct@ccns.ncku.edu.tw)

 

--

譯注:2004年8月,lwIP已釋出1.0.0版,但本文仍有相當的參考價值。待譯者有空時,將會針對最近版本的差異處進行修改。(當然若有 朋友願意指教,更是感激不盡。)

 

原文請見:

http://ccns.ncku.edu.tw/~ct/doc/lwIP/lwIP_Raw_TCPIP.htm

--

 

lwIP 提供了兩組 API ,讓程式和 TCP/IP 程式碼之間進行溝通:循序性API (Sequential API,通常我們可以就稱呼它為“the API”。)(譯注:以下譯文將配合文句流暢性,來決定是否保留原文或簡稱。)與原始TCP/IP介面 (raw TCP/IP interface)。本 文件將對後者進行描述。至於lwIP 0.5以下的版本的API,尚未有文件說明。

 

「循序性API」為一般循序程式提供了使用 lwIP stack 的途徑。它與BSD的socket API頗為類似。它的執行模式是建立在「開啟→讀取→寫入→關閉」這個典範的基礎之上。由於此種方式中,TCP/IP stack事件的基礎乃是其固有的自然天性,因此TCP/IP程式碼和應用程式必定分別存在於「不同的execution context(亦即不同的執行緒)」當中。

 

「原始TCP/IP介面」讓應用程式可以和TCP/IP程式碼更緊密地結合在一起。程式的執行會變成一個在TCP/IP程式碼中進行callback 函式呼叫的事件。這時候,TCP/IP程式碼和應用程式會變成「在同一個執行緒中執行」。相較之下,循序性API的負擔太高,實在不適合小型系統的開發,因為它強迫應用程式遵守多執行緒的典範。

 

使用原始TCP/IP介面,不僅讓程式的執行速度加快,也可以舒緩記憶體的激烈競爭。但「缺點」是程式開發的難度多少增加了一些,而使用原始TCP/IP介面的程式也會更難理解。然而,對於需要在程式大小和記憶體用量上精簡的應用而言,這仍然是個建議的方式。

 

我們可以同時在不同的應用程式中使用這兩種不同的API。事實上,循序性API就是原始TCP/IP介面的一個應用程式實作。 J

 

--- Callbacks

 

程式的執行,是由callback 所趨動的。每個callback都是TCP/IP程式碼中呼叫的一個普通C函式。每個callback函式接受一組TCP或UDP連線狀態作為參數。另外,為了追蹤程式的特定狀態,在呼叫callback函式時也會傳入一個獨立於TCP/IP狀態的程式指定參數。

 

用來設定應用程式連線狀態的函式是:

void tcp_arg(struct tcp_pcb *pcb, void *arg)

它指定了要傳遞給所有其他callback函式的程式特定狀態。pcb代表目前TCP連線的control block(控制區塊),而arg則是要傳入callback的參數。

 

--- TCP/IP連線設定

 

用來將連線設置妥當的函式和循序性API以及BSD socket API中同樣用途的函式十分相似。先用tcp_new()來建立一個新的TCP connection identifier(連線標籤)(也就是一個 PCB,protocol control block)。接著我們可以設定這個PCB,讓它負責傾聽外部連進來的連線,或者是主動連接另外一個host。

 

struct tcp_pcb *tcp_new(void)

這個函式建立一個新的TCP connection identifier (也就是建立一個新的PCB)。假如配置memory給PCB的過程失敗,就傳回null。

 

        err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr,u16_t port)

這個函式將PCB與本地端的IP位址及埠號 bind在一起。將IP位址指定為IP_ADDR_ANY能讓它和所有的本地端IP位址連線bind起來。

 

假如另一個連線也嘗試bind相同的埠號,它會傳回ERR_USE,否則傳回ERR_OK。

 

struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)

令PCB開始傾聽連進來的連線。一旦接受某個連線要求,tcp_accept()所指定的函式會被呼叫起來。PCB必需用tcp_bind()來和本地端的某個埠bind在一起。

 

tcp_listen()會傳回一個新的connection identifier,原本被當成參數傳入function的那一個connection identifier則會被釋放掉。這樣做的理由是,一個負責傾聽的connection所需的記憶體極少,所以tcp_listen()會修正原始連線的記憶體需求,配置一塊較小的記憶體區塊給這個負責傾聽的connection。

 

假如沒有足夠的memory以供配置給這個connection,tcp_listen()將傳回NULL。那麼原本當成參數傳入tcp_listen()的PCB就不會被釋放掉。

 

void tcp_accept(struct tcp_pcb *pcb,err_t (* accept)(void *arg, struct tcp_pcb *newpcb,err_t err))

指定在listening connection中,一旦建立了新的connection,要去呼叫哪個callback函式。

 

err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr,u16_t port, err_t (* connected)(void *arg,struct tcp_pcb *tpcb, err_t err));

將PCB連接遠端host的資訊設置妥當,傳送一個開啟連線的初始SYN值。

 

tcp_connect()會立刻return一個值,而不會等到連線資料全部設定妥當才return。當連線建立時,它會呼叫第四個參數 (connected參數) 所指的函式。假如連線無法正確建立,可能是因為對方host拒絕連線,或是沒有收到該host的回應,那麼 “connected” 函式仍將被呼叫,並傳入相對應的err參數。

 

假如沒有可用的memory供SYN值的queue使用,tcp_connect()會傳回ERR_MEM。假如SYN值enqueue的動作成功了,tcp_connect()就傳回ERR_OK。

 

--- 傳送TCP data

 

TCP data的傳送,是藉由tcp_write()的呼叫來enqueue data。一旦資料成功傳送到遠端主機,應用程式會被通知執行一個指定的callback函式。

 

err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len, u8_t copy)

它會將dataptr所指的data enqueue起來。Data的長度由len參數指定。copy參數的值可以是0或1,代表是否要重新配置一塊記憶體,並將data複製過去。假如copy的值是0,就不會配置新的記憶體,只利用指標將data所在的位址記錄下來。

 

假如資料長度超過傳送用的buffer大小,或者寄送區段的queue長度超過lwipopts.h中定義的上限,tcp_write()的執行將會失敗,並傳回ERR_MEM。Output queue可用的位元組數量可以透過tcp_sndbuf()函式來修改。

 

這個函式的正確操作方式,是傳給它的data不應超過tcp_sndbuf()所定的量。假如它傳回ERR_MEM,應用程式應當等待目前queue裡面的部分資料成功地傳送到對方主機,然後再試一次。

       

--- 接收TCP data

 

TCP data的接收是以callback為基礎──當新的資料到達時,一個應用程式指定的callback函式會被喚起。應用程式接收data之後,要呼叫tcp_recved()來通知TCP去擴張它的receive window並advertise。

 

void tcp_recv(struct tcp_pcb *pcb,err_t (* recv)(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err))

設定新資料抵達時,所要呼叫的callback函式。如果對這個callback函式傳入指向NULL值的pbuf,就代表遠端主機已關閉連線。

 

void tcp_recved(struct tcp_pcb *pcb, u16_t len)

通常在應用程式接收完data後呼叫。len參數指示所接收資料的長度。

 

--- 應用程式輪詢 (polling)

 

當連線idle住的時候(也就是這時候既沒有資料在傳送,也沒有資料在接收),lwIP會藉著呼叫某個特定的callback函式,而不斷地輪詢各個應用程式。這個 功能可以當做watchdog timer,用來將idle太久的connection殺掉,也可以用來等待可用的記憶體。例如,若呼叫tcp_write()後,因為沒有可用的記憶體而造成失敗,應用程式可以利用polling的功能,在connection idle一段時間後,再度喚起tcp_write()。

 

void tcp_poll(struct tcp_pcb *pcb, u8_t interval,err_t (* poll)(void *arg, struct tcp_pcb *tpcb))

指定輪詢時間間隔,以及用來輪詢應用程式的callback函式。時間間隔通常是用TCP coarse所產生的timer shots(一般來說一秒產生兩次)的倍數來指定。interval是10代表應用程式每隔5秒輪詢一次。

 

--- 關閉或中斷連線

 

        err_t tcp_close(struct tcp_pcb *pcb)

關閉連線。假如關閉連線所需的memory不足,就傳回ERR_MEM,然後應用程式應當等待,並利用acknowledgment callback或輪詢機制重新嘗試。假如成功關閉,就傳回ERR_OK。

 

在呼叫tcp_close()之後,TCP程式碼會釋放掉PCB所佔用的記憶體。

 

        void tcp_abort(struct tcp_pcb *pcb)

送一個RST(reset) segment到遠端主機,以便中斷連線。PCB將被釋放。這個函式不會有失敗的情況。

 

假如connection因為某項錯誤而導致中斷,應用程式將會透過err callback而收到警告。會造成中斷的錯誤是像記憶體資源不足這樣的錯誤。錯誤處理的callback函式可以透過tcp_err()來設定。

 

void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,err_t err))

由於錯誤發生時,PCB可能早已被釋放掉,所以錯誤處理的callback函式並不需要接受PCB做為參數。

 

--- 較低階的TCP介面

 

TCP為低階系統提供了一個簡單的介面。在系統初始化時,tcp_init()必須在任何其他的TCP函式被呼叫前先執行。系統執行的過程中必須定期地呼叫tcp_fasttmr()和tcp_slowtmr()這兩個計時器函式。tcp_fasttmr()的執行時間間隔是TCP_FAST_INTERVAL milliseconds(定義在tcp.h中),而tcp_slowtmr()則是每隔TCP_SLOW_INTERVAL milliseconds呼叫一次。

 

--- UDP介面

 

UDP介面與TCP介面相似,但是因為UDP的複雜性較低,因此介面也顯得簡單多了。

 

        struct udp_pcb *udp_new(void)

建立新的UDP PCB以供UDP溝通用。PCB在與本地端位址bind起來、或是連接到遠端位址之後,才進入active狀態。

 

        void udp_remove(struct udp_pcb *pcb)

        移除並釋放所有的PCB。

 

        err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr,u16_t port)

將PCB與本地端位址bind在一起。將ipaddr設為IP_ADDR_ANY代表它將監聽任何的本地端位址。這個函式目前的傳回值一律是ERR_OK。

 

        err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr,u16_t port)

設定PCB的遠地端。這個函式不會造成任何網路流量,只會設定PCB的遠端位址欄位。

 

        err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)

        傳送packet buffer p。它不會釋放掉pbuf。

 

void udp_recv(struct udp_pcb *pcb,void (* recv)(void *arg, struct udp_pcb *upcb,struct pbuf *p,struct ip_addr *addr,u16_t port),void *recv_arg)

        指定UDP diagram接收完成後,應當呼叫的callback函式。


===============================================================
无OS情形下驱动层需要提供的函数如下:

(1)收到数据包的处理函数ethernetif_input(ethif层的输入函数),这个函数调用low_level_output函数,从网卡那里接收数据包,然后根据收到的数据包类型,分别调用ip_input函数和etharp_arp_input函数。这个函数跟ne2k_input函数类型,只是其中的netif->input调用被改ip_input函数。这个函数被作为函数netif_add的参数,或者独立,无论哪种情形,该函数都要被不停地调用;

(2)网卡发送数据函数low_level_output,这个函数在ethif层的初始化函数中被初始化到netif的link_output指针上。
(3)网卡接口数据函数low_level_input,这个函数被ethif层ethernetif_input函数调用,用于从网卡那里接收数据包。在实现这个函数时,需要查询网卡硬件,如果硬件收到数据包,那么从硬件那里将数据包拷贝进pbuf里,然后将该pbuf指针返回,将收到的数据包交给ethernetif_input处理;如果没有收到数据包,那么简单地返回NULL。
(4)ethif层的初始化函数ethernetif_init,这个作为netif_add函数的参数。同时这个函数调用网卡底层初始化函数low_level_init。
(5)ethif层的发送函数ethernetif_output,这个函数跟low_level_output的区别是,ethernetif_output带了一个额外的参数ip地址,一般这个函数都是调用etharp:etharp_output来实现。etharp_output根据ip地址从arp表中查询arp表,得到目标IP地址的MAC地址,然后填入到数据包中,然后调用netif->link_output函数将数据包最终发送出去。
(6)arp_timer函数,这个函数需要被定时地调用。这个函数调用etharp_tmr函数来更新arp表中的表项。
(7)网卡硬件初始化函数low_level_output,这个函数被ethernetif_init调用。
   在协议栈的主循环NetMainLoop中,不断调用ethernetif_input函数,来驱动协议栈。

25

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2010-12-28 14:33 | 显示全部楼层
很精彩,去试试,呵呵

回复

使用道具 举报

949

TA的帖子

0

TA的资源

纯净的硅(高级)

Rank: 6Rank: 6

发表于 2010-12-28 17:56 | 显示全部楼层
好。。讲得的确比其它的要仔细。。。。希望兄弟伙继续更新 。。。。。。。支持一下。。。
只有想不到,没有做不到。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

关闭

站长推荐上一条 /6 下一条

  • 论坛活动 E手掌握

    扫码关注
    EEWORLD 官方微信

  • EE福利  唾手可得

    扫码关注
    EE福利 唾手可得

Archiver|手机版|小黑屋|电子工程世界 ( 京ICP证 060456 )

GMT+8, 2020-1-29 17:17 , Processed in 0.133909 second(s), 17 queries , Gzip On, MemCache On.

快速回复 返回顶部 返回列表