一、 總則 本文件是嵌入式TCP/IP協(xié)議棧的說明文件,嵌入式TCP/IP應用開發(fā)人員可通過閱讀本文件,掌握在嵌入式TCP/IP協(xié)議棧的基礎上開發(fā)服務器和客戶端應用程序,如FTP服務器,WEB服務器,串口服務器等等。 二、 參考文件 1) TCP_IP詳解卷1,2,3 2) RFC 959 (rfc959) - File Transfer Protocol.htm 3) rfc1945- Hypertext Transfer Protocol -- HTTP/1.0 三、 技術說明 1) 用戶應用協(xié)議棧則需要編寫以太網的數據報收發(fā)驅動,就可以使用協(xié)議棧提供的標準SOCKET API,完成服務器和客戶端應用程序的開發(fā)。 2) 協(xié)議棧運行于非操作系統(tǒng)的環(huán)境下,因此它的運行速度與一般采用多任務操作系統(tǒng)的TCP/IP協(xié)議,速度相對說來要快。 3) 協(xié)議棧完成的功能包括ARP,IP,ICMP(ping),TCP,UDP,暫不支持IGMP,RARP。 4) 協(xié)議棧采用C代碼編寫,可方便的移植于各種單片機平臺。 5) 協(xié)議棧在ARM7+RTL8019硬件環(huán)境下測試,并建立了FTP服務器和WEB服務器,性能穩(wěn)定。 6) 協(xié)議棧建立的FTP服務器和WEB服務器與Internet Explorer瀏覽器和ftp.exe相互兼容。 7) 協(xié)議棧每一個SOCKET上建了數據緩沖隊列(數據結構),用于接收SOCKET的并發(fā)數據,實現多SOCKET的并發(fā)數據報處理,可同時運行FTP服務器和WEB服務器。 8) 協(xié)議棧實現了ACK的延時答應(200ms),支持TCP多包發(fā)送和接收,但未支持TCP數據報的失序處理,因此適合局域網內使用。
四、 SOCKET API函數 1) 函數SOCKET * socket(u16 af,u16 type,u16 protocol) 本函數功能是從SOCKET pool中分配一個SOCKET插口,供應用程序使用,其參數說明如下: 1. 參數af,type—無意義,保留為擴充功能使用。 2. 參數protocol—為分配SOCKET的類型,包括TCP_PROTOCOL和UDP_PROTOCOL兩個類型。 3. 返回值:函數執(zhí)行成功,返回SOCKET*指針指向一個SOCKET,失敗返回NULL 2) 函數u16 bind(SOCKET * sock,struct sockaddr * address,u8 len) 本函數功能是將IP地址和端口綁定到一個SOCKET 指針* sock指向的SOCKET。 1. SOCKET * sock—指向被綁定的SOCKET。 2. struct sockaddr * address—指向IP地址和端口。 3. len—無意義,保留為擴充功能使用。 4. 返回值:SUCC。 3) 函數u16 listen(SOCKET * sock, u16 QTY) 本函數功能是啟動被綁定了地址和端口的 SOCKET * sock,觸發(fā)其為監(jiān)聽狀態(tài)。本函數由服務器端應用程序使用。 1. SOCKET * sock—指向被bind的SOCKET。 2. 返回值:SUCC。 4) 函數u16 connect(SOCKET * sock, struct sockaddr * sevaddr,u8 len) 本函數功能是用于建立一個連接,到服務器,服務器的地址和端口由參數sevaddr指定。該函數由客戶端使用。 1. SOCKET * sock—指向被連接的本地SOCKET。 2. struct sockaddr * sevaddr,-- 服務器的地址和端口. 3. u8 len—無意義,保留為擴充功能使用。 4. 返回值:是SOCKET句柄。 5) 函數u16 accept(SOCKET * sock,struct sockaddr * address,u16 *iii) 本函數返回一個已連接的SOCKET句柄,供函數recv(),send()收發(fā)數據使用。 1. SOCKET * sock—指向一個被綁定地址和端口的SOCKET 2. struct sockaddr * address,u16 *iii--無意義,保留為擴充功能使用。 3. 返回值:是SOCKET句柄。 6) 函數u16 recv(u16 handle,u8 * rec_buff,u16 len,u16 i) 指定句柄讀取數據,由TCP使用 1. u16 handle--指定句柄 2. u8 * rec_buff—緩沖區(qū)首地址 3. u16 len—讀取數據的長度 4. u16 i--無意義,保留為擴充功能使用。 5. 返回值:為已讀取的字節(jié)數 7) 函數u8 send(u16 handle,u8 *rec_buff,u16 len,u16 i) 向指定句柄發(fā)送數據,由TCP使用 1. u16 handle--指定句柄 2. u8 * rec_buff—緩沖區(qū)首地址 3. u16 len—發(fā)送數據的長度 4. u16 i--無意義,保留為擴充功能使用。 5. 返回值:是SUCC 8) void close(u16 handle) 發(fā)送FIN主動關閉一個SOCKET連接,handle為被關閉連接的句柄。
9) 函數u16 recvfrom(SOCKET *sock,u8 *rec_buff,u16 len,u8 i,struct sockaddr * address,u16 *addr_len) 從指定SOCKET *sock插口讀取數據,由UDP使用,函數的參數具體情況如下: 1. SOCKET *sock --指向插口的指針 2. u8 * rec_buff—緩沖區(qū)首地址 3. u16 len—讀取數據的長度 4. u16 i--無意義,保留為擴充功能使用。 5. 返回值:為已讀取的字節(jié)數
10) 函數u16 sendto(SOCKET *sock,u8 *rec_buff,u16 len,u8 i,struct sockaddr * address,u16 *addr_len) 向從指定SOCKET *sock插口發(fā)送數據,由UDP使用,函數的參數具體情況如下: 1. SOCKET *sock,--指向插口的指針 2. u8 * rec_buff—緩沖區(qū)首地址 3. u16 len—發(fā)送數據的長度 4. u16 i--無意義,保留為擴充功能使用 5. 返回值:為SUCC
五、 SOCKET API應用舉例 1) 簡單WEB服務器--通過函數TCP_TEST()完成設置本地TCP服備器的IP地址,其過程如下: 1. 調用SOCKET API函數socket(0,0,TCP_PROTOCOL)分配一個SOCKET, 2. 調用SOCKET API函數將TCP server的IP地址與SOCKET綁定,調用函數bind()起動監(jiān)聽。 3. 函數TCP_TEST()通過函數accept()接收網頁獲取請求,調用函數recv()接收HTTP命令,根據命令調用函數send()發(fā)送http網頁。 WEB服務器程序清單 /***************************************************** * 名稱:TCP_TEST() * 功能:設置TCP模塊 * 入口:無 * 出口: 無 ****************************************************************************/ void TCP_SETUP(void) { /*設置本地TCP服備器的IP地址*/ TCP_serveraddr.sin_family = 0; TCP_serveraddr.sin_addr[0] = MY_IP_ADD[0]; TCP_serveraddr.sin_addr[1] = MY_IP_ADD[1]; TCP_serveraddr.sin_addr[2] = MY_IP_ADD[2]; TCP_serveraddr.sin_addr[3] = MY_IP_ADD[3]; TCP_serveraddr.sin_port = 80; /*將TCP server的IP地址與SOCKET綁定*/ t = socket(0,0,TCP_PROTOCOL); iii=bind(t,&TCP_serveraddr,sizeof(TCP_serveraddr)); iii=listen(t,4); } /**************************************************************************** * 名稱:TCP_TEST() * 功能:TCP打開網頁測試 * 入口:無 * 出口: 無 ****************************************************************************/ void TCP_TEST(void) { temp = accept(t,&TCP_clientaddr,&iii);/*accept網頁獲取請求*/ if(temp != 0xffff) { templen = recv(temp,TCP_rec_buff,1024,0); if(TCP_rec_buff[5] == ' ') { send(temp,httpweb,169,0); /*發(fā)送http網頁*/ send(temp,web,395,0); } else if(TCP_rec_buff[5] == '1') { send(temp,httpgif,169,0); /*發(fā)送GIF,BMP圖片背景*/ send(temp,bmp,442,0); } close(temp); } }
2) 簡單UDP服務器—通過函數UDP_TEST()完成設置本地UDP服備器的IP地址和遠端口服務器的IP地址, 其過程如下: 1. 調用SOCKET API函數socket(0,0,TCP_PROTOCOL)分配一個SOCKET 2. 調用SOCKET API函數bind()將UDP server的IP地址與SOCKET綁定,將調用SOCKET API函數enable_a_port_listen(1025)起動監(jiān)聽。 3. 函數UDP_TEST()通過函數recfrom()接收UDP數據報,接收到的UDP數據報調用SOCKET API函數sendto()回傳遠程服務器。 UDP服務器程序清單 /**************************************************************************** * 名稱:UDP_SETUP() * 功能:設置UDP模塊 * 入口:無 * 出口: 無 ****************************************************************************/ void UDP_SETUP(void) { serveraddr.sin_family = 0; /*設置遠端服務器的IP地址*/ serveraddr.sin_addr[0] = 192; serveraddr.sin_addr[1] = 168; serveraddr.sin_addr[2] = 0; serveraddr.sin_addr[3] = 1; serveraddr.sin_port = 1026; s = socket(0,0,UDP_PROTOCOL); clientaddr.sin_family = 0; /*設置本地UDP客戶端的IP地址*/ clientaddr.sin_addr[0] = MY_IP_ADD[0]; clientaddr.sin_addr[1] = MY_IP_ADD[1]; clientaddr.sin_addr[2] = MY_IP_ADD[2]; clientaddr.sin_addr[3] = MY_IP_ADD[3]; clientaddr.sin_port = 1025; /*將本地IP地址與SOCKET綁定*/ iii=bind(s,&clientaddr,sizeof(clientaddr)); enable_a_port_listen(1025); }
/**************************************************************************** * 名稱:UDP_TEST() * 功能:UCP數據報收發(fā)測試 * 入口:無 * 出口: 無 ****************************************************************************/ void UDP_TEST(void) { /*接收UDP數據報*/ len = recvfrom(s,rec_buff,400,0,&serveraddr,&iii); if(len > 0) { /*將接收到的UDP數據報發(fā)送回服務器端*/ sendto(s,rec_buff,len,0,&serveraddr,&iii); } }
|