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