- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
指定一個PCB進(jìn)入監(jiān)聽狀態(tài)。當(dāng)一個遠(yuǎn)端連接訪問時,函數(shù) tcp_accept()指定的回調(diào)函數(shù)將被調(diào)用。在調(diào)用這個函數(shù)之前一定要使用tcp_bind()函數(shù)綁定一個本地IP和端口號。
tcp_listen() 函數(shù)返回一個新的連接標(biāo)識符,原始的pcb會被釋放,這是為了節(jié)省內(nèi)存,使之更適合小內(nèi)存系統(tǒng)。
如果監(jiān)聽連接的內(nèi)存無效,tcp_listen()函數(shù)返回NULL,如果這樣的話,傳入的PCB參數(shù)將不會被釋放。
這個函數(shù)從原理上看也比較簡單,首先是做一些必要的檢查,判斷原始pcb是否已經(jīng)處于連接狀態(tài),如果沒有則申請一塊tcp_pcb類型的內(nèi)存,將原始的必要的pcb內(nèi)容復(fù)制到新的pcb中,設(shè)置新的pcb狀態(tài)為LISTEN,釋放原始的pcb,并將新pcb連接放入已監(jiān)聽隊列。
- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
這個函數(shù)和tcp_listen()函數(shù)相同,只是限制了TCP監(jiān)聽隊列連接個數(shù),這個個數(shù)由backlog參數(shù)指定。為了使用它,你必須在你的lwipopt.h中設(shè)置TCP_LISTEN_BACKLOG=1。
- void tcp_accepted(struct tcp_pcb *pcb)
通知lwIP一個傳入的連接已經(jīng)被接受。通常這個函數(shù)在“accept()”函數(shù)的回調(diào)函數(shù)中被調(diào)用。這允許lwIP處理自身內(nèi)部的任務(wù)。比如,允許更多傳入的連接進(jìn)入監(jiān)聽隊列。
- void tcp_accept(struct tcp_pcb *pcb,
err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
err_t err))
指定應(yīng)在偵聽連接上的一個新的連接到達(dá)時調(diào)用的回調(diào)函數(shù)。
- 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));
設(shè)置打開連接的pcb連接到遠(yuǎn)程主機(jī)并發(fā)送初始的SYN段。
函數(shù)tcp_connect() 會立即返回;它并不等待這個連接是否被正確設(shè)置。相反的,當(dāng)連接正確建立后它將調(diào)用第四個參數(shù)("connected"參數(shù))指定的函數(shù)。如果這個連接不能正確的建立,可能是主機(jī)拒絕這個連接或者主機(jī)沒有響應(yīng),"connected"函數(shù)將被調(diào)用并設(shè)置一個相應(yīng)的參數(shù)。
當(dāng)入隊的SYN段內(nèi)存不可用時,tcp_connect()函數(shù)能返回ERR_MEM,表示連接沒有正確建立。如果SYN成功入隊,tcp_connect()函數(shù)返回ERR_OK。
---TCP數(shù)據(jù)發(fā)送函數(shù)
lwIP會調(diào)用tcp_write()函數(shù)來發(fā)送隊列中的數(shù)據(jù)。當(dāng)數(shù)據(jù)成功的發(fā)送到遠(yuǎn)程主機(jī),會調(diào)用一個指定的回調(diào)函數(shù)來通知應(yīng)用程序。
- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len,
u8_t copy)
參數(shù)"dataptr"指向數(shù)據(jù)隊列;參數(shù)"len"傳遞數(shù)據(jù)的長度;參數(shù)"copy"的值為0或者1,表明是否需要申請新的內(nèi)存用于數(shù)據(jù)的拷貝。如果這個參數(shù)為0,則不需要申請新的內(nèi)存,此時數(shù)據(jù)只能使用指針來引用。
如果數(shù)據(jù)長度超過當(dāng)前發(fā)送緩存字節(jié)數(shù)或者要發(fā)送的段隊列長度超過lwipopts.h中定義的上限值,tcp_write()函數(shù)執(zhí)行失敗并返回ERR_MEN。可以使用tcp_sndbuf()函數(shù)來返回輸出隊列有效的字節(jié)數(shù)。
使用這個函數(shù)的正確方法是根據(jù)tcp_sndbuf() 函數(shù)返回的字節(jié)數(shù)來發(fā)送數(shù)據(jù)。如果函數(shù)返回ERR_MEM,應(yīng)用程序應(yīng)該等待直到當(dāng)前隊列數(shù)據(jù)成功的被遠(yuǎn)程主機(jī)收到然后嘗試重新發(fā)送一次。
- void tcp_sent(struct tcp_pcb *pcb,
err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
u16_t len))
當(dāng)遠(yuǎn)程主機(jī)成功接收(也就是應(yīng)答信號)到數(shù)據(jù)時,該函數(shù)指定的回調(diào)函數(shù)被調(diào)用。傳送給回調(diào)函數(shù)的"len"參數(shù)給出了上一次已經(jīng)被確認(rèn)的發(fā)送的最大字節(jié)數(shù)。
--TCP數(shù)據(jù)接收函數(shù)
TCP數(shù)據(jù)接收是基于回調(diào)函數(shù)的---當(dāng)一個新的數(shù)據(jù)接收到時,應(yīng)用程序指定的回調(diào)函數(shù)被調(diào)用。當(dāng)應(yīng)用程序接收到數(shù)據(jù)后,它必須調(diào)用tcp_recved()函數(shù)來指示接收數(shù)據(jù)的大小。
- void tcp_recv(struct tcp_pcb *pcb,
err_t (* recv)(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err))
當(dāng)接收到數(shù)據(jù)時,本函數(shù)設(shè)置的回調(diào)函數(shù)將被調(diào)用。如果傳遞給回調(diào)函數(shù)一個NULL pbuf則說明遠(yuǎn)程主機(jī)關(guān)閉了這個連接。如果函數(shù)正常運行并且回調(diào)函數(shù)返回ERR_OK,則必須釋放這個pbuf,如果其它情況,必須保存這個pbuf,這樣才能讓lwIP內(nèi)核保存它以供應(yīng)用程序檢查并恢復(fù)錯誤。
- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
當(dāng)應(yīng)用程序接收到數(shù)據(jù)后必須調(diào)用這個函數(shù)。參數(shù)"len"表明接收到的數(shù)據(jù)的長度。
--- 應(yīng)用程序輪詢函數(shù)
當(dāng)一個連接空的時候(也就是說,既沒有數(shù)據(jù)接收也沒有數(shù)據(jù)發(fā)送),lwIP會通過調(diào)用一個指定的回調(diào)函數(shù)來重復(fù)輪詢應(yīng)用程序。這可以用作一個看門狗定時器,用來終止空閑時間太長的連接;或者用作等待內(nèi)存有效的一種方法。舉例來說,如果調(diào)用tcp_write()函數(shù)時因為內(nèi)存無效而失敗,應(yīng)用程序可以使用輪詢功能在連接空閑的時候再次調(diào)用tcp_write()。
- void tcp_poll(struct tcp_pcb *pcb, u8_t interval,
err_t (* poll)(void *arg, struct tcp_pcb *tpcb))
指定輪詢間隔和應(yīng)用程序輪詢時調(diào)用的回調(diào)函數(shù)。這個間隔是以TCP粗粒度定時器為單位的,即500毫秒一次。如果參數(shù)"interval"的值為10,則意味著每5秒輪詢一次應(yīng)用程序。
---關(guān)閉和終止連接函數(shù)
- err_t tcp_close(struct tcp_pcb *pcb)
關(guān)閉連接。如果關(guān)閉的連接內(nèi)存無效,函數(shù)返回ERR_MEM,如果是這樣的話,應(yīng)用程序應(yīng)該等待并通過使用acknowledgment回調(diào)函數(shù)或者輪詢功能重新關(guān)閉連接。如果連接關(guān)閉成功,函數(shù)返回WRR_OK。
TCP內(nèi)核調(diào)用tcp_close()后,參數(shù)"pcb"指定的連接被解除。
- void tcp_abort(struct tcp_pcb *pcb)
通過向遠(yuǎn)程主機(jī)發(fā)送一個RST(復(fù)位)段來終止連接。這個函數(shù)從不會失敗。
如果這個連接因為一個錯誤而被終止,則應(yīng)用程序可以通過err回調(diào)函數(shù)靈活的處理這個事件。通常一個連接因錯誤而終止的原因是內(nèi)存不足。這時使用tcp_err()函數(shù)設(shè)置的回調(diào)函數(shù)被調(diào)用。
- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
err_t err))
指定一個處理錯誤的回調(diào)函數(shù),該回調(diào)函數(shù)不能得到本函數(shù)的"pcb"作為它的參數(shù),因為這個pcb可能已經(jīng)被解除。
--- 低層次TCP接口
在系統(tǒng)的較低層,TCP提供一個簡單的接口。在系統(tǒng)初始化的時候,任何其他TCP函數(shù)被調(diào)用之前必須先調(diào)用tcp_init()函數(shù)。當(dāng)系統(tǒng)已經(jīng)運行,兩個定時器函數(shù)tcp_fasttmr() 和tcp_slowtmr()必須定期被調(diào)用。tcp_fasttmr()函數(shù)必須每隔TCP_FAST_INTERVAL(定義在tcp.h中)個毫秒被調(diào)用一次,tcp_slowtmr() 函數(shù)必須每隔TCP_SLOW_INTERVAL個毫秒被調(diào)用一次。
--- UDP 接口
相比之下,UDP接口要比TCP接口類似,但UDP在低層次的復(fù)雜程度上明顯比TCP簡單。
- struct udp_pcb *udp_new(void)ige
創(chuàng)建一個用于UDP通訊的UDP pcb。這個pcb直到綁定本地地址或者連接到遠(yuǎn)程地址后才被激活。
- void udp_remove(struct udp_pcb *pcb)
刪除一個指定的連接。
-err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
為pcb綁定一個本地地址。參數(shù)"ipaddr"為IP_ADDR_ANY時,指定可以監(jiān)聽任何本地IP地址。這個函數(shù)一般都會返回ERR_OK。
- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
設(shè)置pcb連接到遠(yuǎn)程主機(jī)。這個函數(shù)不產(chǎn)生任何流量,僅設(shè)置pcb的遠(yuǎn)程地址。
- err_t udp_disconnect(struct udp_pcb *pcb)
刪除遠(yuǎn)程端的pcb。這個函數(shù)不產(chǎn)生任何流量,近視刪除pcb的遠(yuǎn)程地址。
- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
發(fā)送pbuf結(jié)構(gòu)指針p指向的數(shù)據(jù)。這個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)
當(dāng)接收到一個數(shù)據(jù)包后,該函數(shù)指定的回調(diào)函數(shù)將被調(diào)用。
---系統(tǒng)初始化
一個完整通用的lwIP初始化步驟是不可能實現(xiàn)的,因為它還取決于配置文件(lwipopts.h)的編寫以及初始化額外運行時的環(huán)境(例如硬件定時器)。
當(dāng)你使用RAW API時,我們可以給你一些建議。
我們假設(shè)你使用一個單一的以太網(wǎng)netif和UDP、TCP傳輸層、IPv4和DHCP客戶端。
安以下順序調(diào)用這些函數(shù):
- stats_init()
清楚運行時被收集的統(tǒng)計結(jié)構(gòu)。
- sys_init()
沒有多大用處,因為我們在lwipopts.h中設(shè)置NO_SYS 1
Not of much use since we set the NO_SYS 1 option in lwipopts.h, to be called for easy
configuration changes.
- mem_init()
通過定義MEM_SIZE初始化動態(tài)存儲堆
- memp_init()
通過定義MEMP_NUM_x初始化內(nèi)存池。
- pbuf_init()
通過定義PBUF_POOL_SIZE初始化pbuf內(nèi)存池。
- etharp_init()
初始化ARP表和隊列。
注:在這個初始化之后你必須每隔 ARP_TMR_INTERVAL(5秒)個周期間隔調(diào)用etharp_tmr 函數(shù)。
- ip_init()
不常用,處理將要放生的改變時被調(diào)用。
- udp_init()
清除UDP PCB列表。
- tcp_init()
清除TCP PCB列表并清除一些內(nèi)部定時器。
注:在這個初始化函數(shù)之后,你必須按預(yù)先確定的每個周期內(nèi)調(diào)用tcp_fasttmr() 和 tcp_slowtmr()函數(shù)。
- netif_add(struct netif *netif, struct ip_addr *ipaddr,
struct ip_addr *netmask, struct ip_addr *gw,
void *state, err_t (* init)(struct netif *netif),
err_t (* input)(struct pbuf *p, struct netif *netif))
向netif_list列表中增加你的網(wǎng)絡(luò)接口。分配一個netif結(jié)構(gòu)體并傳遞一個指向這個結(jié)構(gòu)體的指針作為第一個參數(shù)。當(dāng)使用DHCP時給定的ip_addr結(jié)構(gòu)體會被清除,或者用其它數(shù)據(jù)填充它們。"state"指針可能為NULL。
函數(shù)指針"init"必須指向你的以太網(wǎng)netif接口初始化函數(shù),下面舉例說用該函數(shù)的應(yīng)用。
err_t netif_if_init(struct netif *netif)
{
u8_t i;
for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i];
init_my_eth_device();
return ERR_OK;
}
為使用以太網(wǎng)驅(qū)動器(For ethernet drivers),函數(shù)指針"input"必須指向"netif/etharp.h"中聲明的ethernet_input() 函數(shù)。其它驅(qū)動器(Other drivers)必須使用"lwip/ip.h"中聲明的ip_input()函數(shù)。
- netif_set_default(struct netif *netif)
注冊默認(rèn)網(wǎng)絡(luò)接口
- netif_set_up(struct netif *netif)
當(dāng)netif完全配置后,這個函數(shù)必須被調(diào)用。
- dhcp_start(struct netif *netif)
在第一次調(diào)用時為這個接口創(chuàng)建一個新的DHCP客戶端。
注:啟動這個客戶端后你必須按照預(yù)先設(shè)定的間隔周期性的調(diào)用dhcp_fine_tmr() 和dhcp_coarse_tmr()函數(shù)。
你可以通過結(jié)構(gòu)體netif->dhcp查看真實的DHCP狀態(tài)。
--- 優(yōu)化提示
首先要做的是優(yōu)化src/core/inet.c中的lwip_standard_checksum()程序。你可以使用
#define LWIP_CHKSUM <your_checksum_routine>
來重寫這個標(biāo)準(zhǔn)函數(shù)。
inet.c中使用C語言編寫的例子,你也可以使用匯編語言編寫。
RFC1071是這個主題的很好的介紹。
如果你使用小端處理器,另一個有效的改善是用匯編語言或者內(nèi)聯(lián)函數(shù)代替htons() 和 htonl()函數(shù)。
#define LWIP_PLATFORM_BYTESWAP 1
#define LWIP_PLATFORM_HTONS(x) <your_htons>
#define LWIP_PLATFORM_HTONL(x) <your_htonl>
如果你的網(wǎng)絡(luò)讀到的速度比最大線速還要大,檢查你的網(wǎng)絡(luò)接口。如果硬件不能提供良好的服務(wù),會經(jīng)?焖俚陌l(fā)生緩沖區(qū)溢出現(xiàn)象。舉例來說,當(dāng)使用cs8900處理器時,調(diào)用cs8900if_service(ethif)函數(shù)可能很頻繁出現(xiàn)上述現(xiàn)象。當(dāng)使用的RTOS允許cs8900使用中斷喚醒一個服務(wù)于一個你的使用一個二進(jìn)制信號量或事件標(biāo)志的驅(qū)動程序的高優(yōu)先級任務(wù)。
當(dāng)產(chǎn)品發(fā)布時,建議設(shè)置LWIP_STATS為0。
實際當(dāng)中,太多的函數(shù)會造成難以維護(hù),所以會適量精簡。
|