1、網(wǎng)卡組件(netdev)
netdev 組件主要作用是解決設(shè)備多網(wǎng)卡連接時網(wǎng)絡(luò)連接問題,用于統(tǒng)一管理各個網(wǎng)卡信息與網(wǎng)絡(luò)連接狀態(tài),并且提供統(tǒng)一的網(wǎng)卡調(diào)試命令接口。其主要功能特點如下所示:
抽象網(wǎng)卡概念,每個網(wǎng)絡(luò)連接設(shè)備可注冊唯一網(wǎng)卡。
提供多種網(wǎng)絡(luò)連接信息查詢,方便用戶實時獲取當前網(wǎng)卡網(wǎng)絡(luò)狀態(tài);
建立網(wǎng)卡列表和默認網(wǎng)卡,可用于網(wǎng)絡(luò)連接的切換;
提供多種網(wǎng)卡操作接口(設(shè)置 IP、DNS 服務(wù)器地址,設(shè)置網(wǎng)卡狀態(tài)等);
統(tǒng)一管理網(wǎng)卡調(diào)試命令(ping、ifconfig、netstat、dns 等命令);
網(wǎng)卡概念:
網(wǎng)卡概念介紹之前先了解協(xié)議棧相關(guān)概念,協(xié)議棧是指網(wǎng)絡(luò)中各層協(xié)議的總和,每種協(xié)議棧反映了不同的網(wǎng)絡(luò)數(shù)據(jù)交互方式,RT-Thread 系統(tǒng)中目前支持三種協(xié)議棧類型:lwIP 協(xié)議棧、AT Socket 協(xié)議棧、WIZnet TCP/IP硬件協(xié)議棧。每種協(xié)議棧對應(yīng)一種協(xié)議簇類型(family),上述協(xié)議棧分別對應(yīng)的協(xié)議簇類型為:AF_INET、AF_AT、AF_WIZ。
網(wǎng)卡的初始化和注冊建立在協(xié)議簇類型上,所以每種網(wǎng)卡對應(yīng)唯一的協(xié)議簇類型。Socket 套接字描述符的創(chuàng)建建立在 netdev 網(wǎng)卡基礎(chǔ)上,所以每個創(chuàng)建的 Socket 對應(yīng)唯一的網(wǎng)卡。協(xié)議簇、網(wǎng)卡和 socket 之間關(guān)系如下圖所示:
1.1 netdev數(shù)據(jù)結(jié)構(gòu)
每個網(wǎng)卡對應(yīng)唯一的網(wǎng)卡結(jié)構(gòu)體對象,其中包含該網(wǎng)卡的主要信息和實時狀態(tài),用于后面網(wǎng)卡信息的獲取和設(shè)置。
網(wǎng)卡狀態(tài):
up/down:底層網(wǎng)卡初始化完成之后置為 up 狀態(tài),用于判斷網(wǎng)卡開啟還是禁用。
link_up/link_down:用于判斷網(wǎng)卡設(shè)備是否具有有效的鏈路連接,連接后可以與其他網(wǎng)絡(luò)設(shè)備進行通信。該狀態(tài)一般由網(wǎng)卡底層驅(qū)動設(shè)置。
internet_up/internet_down:用于判斷設(shè)備是否連接到因特網(wǎng),接入后可以與外網(wǎng)設(shè)備進行通信。
dhcp_enable/dhcp_disable:用于判斷當前網(wǎng)卡設(shè)備是否開啟 DHCP 功能支持。
1.2 網(wǎng)卡列表和默認網(wǎng)卡
1/* The list of network interface device */
2struct netdev *netdev_list;
3/* The default network interface device */
4struct netdev *netdev_default;
為了方便網(wǎng)卡的管理和控制,netdev 組件中提供網(wǎng)卡列表用于統(tǒng)一管理各個網(wǎng)卡設(shè)備,系統(tǒng)中每個網(wǎng)卡在初始化時會創(chuàng)建和注冊網(wǎng)卡對象到 netdev 組件網(wǎng)卡列表中。
網(wǎng)卡列表中有且只有一個默認網(wǎng)卡,一般為系統(tǒng)中第一個注冊的網(wǎng)卡,可以通過 netdev_set_default() 函數(shù)設(shè)置默認網(wǎng)卡,默認網(wǎng)卡的主要作用是確定優(yōu)先使用的進行網(wǎng)絡(luò)通訊的網(wǎng)卡類型,方便網(wǎng)卡的切換和網(wǎng)卡信息的獲取。
1.3 網(wǎng)卡注冊
1int netdev_register(struct netdev *netdev, const char *name, void *user_data);
參數(shù)描述
netdev網(wǎng)卡對象
name網(wǎng)卡名稱
user_data用戶使用數(shù)據(jù)
返回——
0網(wǎng)卡注冊成功
-1網(wǎng)卡注冊失敗
將網(wǎng)卡掛載到網(wǎng)卡列表(netdev_list)和默認網(wǎng)卡(netdev_default)。
該函數(shù)不需要在用戶層調(diào)用,一般為網(wǎng)卡驅(qū)動初始化完成之后自動調(diào)用,如 esp8266 網(wǎng)卡的注冊在 esp8266 設(shè)備網(wǎng)絡(luò)初始化之后自動完成。
1.2 注銷網(wǎng)卡
該函數(shù)可以在網(wǎng)卡使用時,注銷網(wǎng)卡的注冊,即從網(wǎng)卡列表中刪除對應(yīng)網(wǎng)卡,注銷網(wǎng)卡的接口如下所示:
1int netdev_unregister(struct netdev *netdev);
1.3 獲取網(wǎng)卡對象
通過狀態(tài)獲取第一個匹配的網(wǎng)卡對象
1struct netdev *netdev_get_first_by_flags(uint16_t flags);
獲取第一個指定協(xié)議簇類型的網(wǎng)卡對象
1struct netdev *netdev_get_by_family(int family);
通過 IP 地址獲取網(wǎng)卡對象
1struct netdev *netdev_get_by_ipaddr(ip_addr_t *ip_addr);
該函數(shù)主要用于 bind 函數(shù)綁定指定 IP 地址時獲取網(wǎng)卡狀態(tài)信息的情況。
通過名稱獲取網(wǎng)卡對象
1struct netdev *netdev_get_by_name(const char *name);
1.4 設(shè)置網(wǎng)卡信息
設(shè)置默認網(wǎng)卡
1void netdev_set_default(struct netdev *netdev);
設(shè)置網(wǎng)卡 up/down 狀態(tài)
1int netdev_set_up(struct netdev *netdev);
2int netdev_set_down(struct netdev *netdev);
設(shè)置網(wǎng)卡 DHCP 功能狀態(tài)
DHCP 即動態(tài)主機配置協(xié)議,如果開啟該網(wǎng)卡 DHCP 功能將無法設(shè)置該網(wǎng)卡 IP 、網(wǎng)關(guān)和子網(wǎng)掩碼地址等信息,如果關(guān)閉該功能則可以設(shè)置上述信息。
1int netdev_dhcp_enabled(struct netdev *netdev, rt_bool_t is_enabled);
設(shè)置網(wǎng)卡地址信息
設(shè)置指定網(wǎng)卡地址 IP 、網(wǎng)關(guān)和子網(wǎng)掩碼地址,需要在網(wǎng)卡關(guān)閉 DHCP 功能狀態(tài)使用。
1/* 設(shè)置網(wǎng)卡 IP 地址 */
2int netdev_set_ipaddr(struct netdev *netdev, const ip_addr_t *ipaddr);
3/* 設(shè)置網(wǎng)卡網(wǎng)關(guān)地址 */
4int netdev_set_gw(struct netdev *netdev, const ip_addr_t *gw);
5/* 設(shè)置網(wǎng)卡子網(wǎng)掩碼地址 */
6int netdev_set_netmask(struct netdev *netdev, const ip_addr_t *netmask);
7/* 設(shè)置網(wǎng)卡 DNS 服務(wù)器地址,主要用于網(wǎng)卡域名解析功能 */
8int netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, const ip_addr_t *dns_server);
設(shè)置網(wǎng)卡回調(diào)函數(shù)
可以用于設(shè)備網(wǎng)卡狀態(tài)改變時調(diào)用的回調(diào)函數(shù),狀態(tài)的改變包括:up/down、 link_up/link_down、internet_up/internet_down、dhcp_enable/dhcp_disable 等。
1ypedef void (*netdev_callback_fn )(struct netdev *netdev, enum netdev_cb_type type);
2void netdev_set_status_callback(struct netdev *netdev, netdev_callback_fn status_callback);
1.5 獲取網(wǎng)卡信息
判斷網(wǎng)卡是否為 up 狀態(tài)
1#define netdev_is_up(netdev)
判斷網(wǎng)卡是否為 link_up 狀態(tài)
1#define netdev_is_link_up(netdev)
判斷網(wǎng)卡是否為 internet_up 狀態(tài)
1#define netdev_is_internet_up(netdev)
判斷網(wǎng)卡 DHCP 功能是否開啟
1#define netdev_is_dhcp_enable(netdev)
1.6 默認網(wǎng)卡自動切換
單網(wǎng)卡模式下,開啟和關(guān)閉默認網(wǎng)卡自動切換功能無明顯效果。
多網(wǎng)卡模式下,如果開啟默認網(wǎng)卡自動切換功能,當前默認網(wǎng)卡狀態(tài)改變?yōu)?down 或 link_down 時,默認網(wǎng)卡會切換到網(wǎng)卡列表中第一個狀態(tài)為 up 和 link_up 的網(wǎng)卡。這樣可以使一個網(wǎng)卡斷開后快速切換到另一個可用網(wǎng)卡,簡化用戶應(yīng)用層網(wǎng)卡切換操作。如果未開啟該功能,則不會自動切換默認網(wǎng)卡。
1.7 FinSH 命令
2、套接字組件(SAL)
socket 編程模型如下圖所示:
客戶端使用流程:
socket() 創(chuàng)建一個 socket,返回套接字的描述符,并為其分配系統(tǒng)資源。
connect() 向服務(wù)器發(fā)出連接請求。
send()/recv() 與服務(wù)器進行通信。
closesocket() 關(guān)閉 socket,回收資源。
服務(wù)器使用流程:
socket() 創(chuàng)建一個 socket,返回套接字的描述符,并為其分配系統(tǒng)資源。
bind() 將套接字綁定到一個本地地址和端口上。
listen() 將套接字設(shè)為監(jiān)聽模式并設(shè)置監(jiān)聽數(shù)量,準備接收客戶端請求。
accept() 等待監(jiān)聽的客戶端發(fā)起連接,并返回已接受連接的新套接字描述符。
recv()/send() 用新套接字與客戶端進行通信。
closesocket() 關(guān)閉 socket,回收資源。
2.1 SAL 簡介
SAL 組件主要功能特點:
抽象、統(tǒng)一多種網(wǎng)絡(luò)協(xié)議棧接口;
提供 Socket 層面的 TLS 加密傳輸特性;
支持標準 BSD Socket API;
統(tǒng)一的 FD 管理,便于使用 read/write poll/select 來操作網(wǎng)絡(luò)功能;
SAL 網(wǎng)絡(luò)框架:
2.2 SAL 原理
多協(xié)議棧接入與接口函數(shù)統(tǒng)一抽象功能:
對于不同的協(xié)議?;蚓W(wǎng)絡(luò)功能實現(xiàn),網(wǎng)絡(luò)接口的名稱可能各不相同,以 connect 連接函數(shù)為例,lwIP 協(xié)議棧中接口名稱為 lwip_connect ,而 AT Socket 網(wǎng)絡(luò)實現(xiàn)中接口名稱為 at_connect。SAL 組件提供對不同協(xié)議?;蚓W(wǎng)絡(luò)實現(xiàn)接口的抽象和統(tǒng)一,組件在 socket 創(chuàng)建時通過判斷傳入的協(xié)議簇(domain)類型來判斷使用的協(xié)議棧或網(wǎng)絡(luò)功能,完成 RT-Thread 系統(tǒng)中多協(xié)議的接入與使用。
目前 SAL 組件支持的協(xié)議?;蚓W(wǎng)絡(luò)實現(xiàn)類型有:lwIP 協(xié)議棧、AT Socket 協(xié)議棧、WIZnet 硬件 TCP/IP 協(xié)議棧。
在 Socket 中,它使用一個套接字來記錄網(wǎng)絡(luò)的一個連接,套接字是一個整數(shù),就像我們操作文件一樣,利用一個文件描述符,可以對它打開、讀、寫、關(guān)閉等操作,類似的,在網(wǎng)絡(luò)中,我們也可以對 Socket 套接字進行這樣子的操作,比如開啟一個網(wǎng)絡(luò)的連接、讀取連接主機發(fā)送來的數(shù)據(jù)、向連接的主機發(fā)送數(shù)據(jù)、終止連接等操作。
socket文件描述符的操作接口如下所示,在創(chuàng)建套接字的時候進行初始化,當使用虛擬文件系統(tǒng)的接口write(),read(),close()等接口時,會調(diào)用如下相應(yīng)接口:
1const struct dfs_file_ops _net_fops =
2{
3 NULL, /* open */
4 dfs_net_close,
5 dfs_net_ioctl,
6 dfs_net_read,
7 dfs_net_write,
8 NULL,
9 NULL, /* lseek */
10 NULL, /* getdents */
11 dfs_net_poll,
12};
創(chuàng)建套接字接口:
1int socket(int domain, int type, int protocol);
socket調(diào)用的流程大致如下:socket-》sal_socket-》at_socket/lwip_socket.
創(chuàng)建一個BSD套接字
分配一個fd文件描述符
初始化fd文件描述符
創(chuàng)建套接字,然后將其放入dfs_fd
上述為標準 BSD Socket API 中 socket 創(chuàng)建函數(shù)的定義,domain 表示協(xié)議域又稱為協(xié)議簇(family),用于判斷使用哪種協(xié)議?;蚓W(wǎng)絡(luò)實現(xiàn),AT Socket 協(xié)議棧使用的簇類型為 AF_AT,lwIP 協(xié)議棧使用協(xié)議簇類型有 AF_INET等,WIZnet 協(xié)議棧使用的協(xié)議簇類型為 AF_WIZ。
對于不同的軟件包,socket 傳入的協(xié)議簇類型可能是固定的,不會隨著 SAL 組件接入方式的不同而改變。為了動態(tài)適配不同協(xié)議?;蚓W(wǎng)絡(luò)實現(xiàn)的接入,SAL 組件中對于每個協(xié)議棧或者網(wǎng)絡(luò)實現(xiàn)提供兩種協(xié)議簇類型匹配方式:主協(xié)議簇類型和次協(xié)議簇類型。socket 創(chuàng)建時先判斷傳入?yún)f(xié)議簇類型是否存在已經(jīng)支持的主協(xié)議類型,如果是則使用對應(yīng)協(xié)議?;蚓W(wǎng)絡(luò)實現(xiàn),如果不是判斷次協(xié)議簇類型是否支持。目前系統(tǒng)支持協(xié)議簇類型如下:
lwIP 協(xié)議棧:family = AF_INET、sec_family = AF_INET
AT Socket 協(xié)議棧:family = AF_AT、sec_family = AF_INET WIZnet
硬件 TCP/IP 協(xié)議棧:family = AF_WIZ、sec_family = AF_INET
鏈接服務(wù)器接口:
1int connect(int s, const struct sockaddr *name, socklen_t namelen)
2
connect調(diào)用的流程大致如下:connect-》sal_connect-》at_connect/lwip_connect.
connect:SAL 組件對外提供的抽象的 BSD Socket API,用于統(tǒng)一 fd 管理;
sal_connect:SAL 組件中 connect 實現(xiàn)函數(shù),用于調(diào)用底層協(xié)議棧注冊的 operation 函數(shù)。
at_connect/lwip_connect:底層協(xié)議棧提供的層 connect 連接函數(shù),在網(wǎng)卡初始化完成時注冊到 SAL 組件中,最終調(diào)用的操作函數(shù)。
2.3 數(shù)據(jù)結(jié)構(gòu)
網(wǎng)絡(luò)接口設(shè)備協(xié)議簇數(shù)據(jù)結(jié)構(gòu):
SAL 套接字表數(shù)據(jù)結(jié)構(gòu):
1static struct sal_socket_table socket_table;
初始化sal套接字:
1int sal_init(void);
該初始化函數(shù)主要是對 SAL 組件進行初始化,動態(tài)申請socket_table對象。支持組件重復(fù)初始化判斷,完成對組件中使用的互斥鎖等資源的初始化。
如果AT組件使用了SAL 套接字,則在sal_at_netdev_set_pf_info(netdev)函數(shù)對網(wǎng)絡(luò)接口設(shè)備協(xié)議族信息(struct sal_proto_family)進行賦值。
如果LWIP組件使用了SAL 套接字,則在sal_lwip_netdev_set_pf_info(struct netdev *netdev)函數(shù)對網(wǎng)絡(luò)接口設(shè)備協(xié)議族信息(struct sal_proto_family)進行賦值。
2.4 SAL Socket API 介紹
1int sal_socket(int domain, int type, int protocol)
在套接字表中分配一個新的套接字和注冊的套接字選項
通過套接字描述符獲取sal套接字對象
初始化sal套接字對象
打開有效的網(wǎng)絡(luò)接口套接字(at_socket/lwip_socket)
1int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen)
通過套接字描述符獲取套接字對象
檢查輸入ipaddr是否是默認的netdev ipaddr,如果不是根據(jù)ip地址獲取新的網(wǎng)卡設(shè)備
通過網(wǎng)絡(luò)接口設(shè)備檢查和獲取協(xié)議族
調(diào)用對應(yīng)驅(qū)動的bind接口(at_bind/lwip_bind)
1int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen)
通過套接字描述符獲取套接字對象
調(diào)用對應(yīng)驅(qū)動的connect接口(at_connect/lwip_connect)
其他接口:
1int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen)
2int sal_shutdown(int socket, int how)
3int sal_getpeername (int socket, struct sockaddr *name, socklen_t *namelen);
4int sal_getsockname (int socket, struct sockaddr *name, socklen_t *namelen);
5int sal_getsockopt (int socket, int level, int optname, void *optval, socklen_t *optlen);
6int sal_setsockopt (int socket, int level, int optname, const void *optval, socklen_t optlen);
7int sal_listen(int socket, int backlog);
8int sal_recvfrom(int socket, void *mem, size_t len, int flags,
9 struct sockaddr *from, socklen_t *fromlen);
10int sal_sendto(int socket, const void *dataptr, size_t size, int flags,
11 const struct sockaddr *to, socklen_t tolen);
12int sal_socket(int domain, int type, int protocol);
13int sal_closesocket(int socket);
14int sal_ioctlsocket(int socket, long cmd, void *arg);
2.5 BSD Socket API 介紹
創(chuàng)建套接字(socket)
1int socket(int domain, int type, int protocol);
創(chuàng)建一個BSD套接字
分配一個fd文件描述符
通過sal_socket()接口創(chuàng)建套接字
初始化fd文件描述符,然后將套接字socket放入dfs_fd
綁定套接字(bind)
1int bind(int s, const struct sockaddr *name, socklen_t namelen);
調(diào)用sal_bind()
建立連接(connect)
1int connect(int s, const struct sockaddr *name, socklen_t namelen)sal_connect
調(diào)用sal_connect()
監(jiān)聽套接字(listen)
1int listen(int s, int backlog)
接收連接(accept)
1int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
TCP 數(shù)據(jù)發(fā)送(send)
1int send(int s, const void *dataptr, size_t size, int flags)
TCP 數(shù)據(jù)接收(recv)
1int recv(int s, void *mem, size_t len, int flags)
UDP 數(shù)據(jù)發(fā)送(sendto)
1int sendto(int s, const void *dataptr, size_t size, int flags, const struct sockaddr *to, socklen_t tolen)
UDP 數(shù)據(jù)接收(recvfrom)
1int recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
關(guān)閉套接字(closesocket)
1int closesocket(int s)
按設(shè)置關(guān)閉套接字(shutdown)
1int shutdown(int s, int how)
設(shè)置套接字選項(setsockopt)
1int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
獲取套接字選項(getsockopt)
1int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
獲取遠端地址信息(getpeername)
1int getpeername(int s, struct sockaddr *name, socklen_t *namelen)
獲取本地地址信息(getsockname)
1int getsockname(int s, struct sockaddr *name, socklen_t *namelen)
配置套接字參數(shù)(ioctlsocket)
1int ioctlsocket(int s, long cmd, void *arg)
3、AT組件
AT 命令集是一種應(yīng)用于 AT 服務(wù)器(AT Server)與 AT 客戶端(AT Client)間的設(shè)備連接與數(shù)據(jù)通信的方式。其基本結(jié)構(gòu)如下圖所示:
AT 命令由三個部分組成,分別是:前綴、主體和結(jié)束符。其中前綴由字符 AT 構(gòu)成;主體由命令、參數(shù)和可能用到的數(shù)據(jù)組成;結(jié)束符一般為(“ ”)。
響應(yīng)數(shù)據(jù): AT Client 發(fā)送命令之后收到的 AT Server 響應(yīng)狀態(tài)和信息。
URC 數(shù)據(jù):AT Server 主動發(fā)送給 AT Client 的數(shù)據(jù),一般出現(xiàn)在一些特殊的情況,比如 WIFI 連接斷開、TCP 接收數(shù)據(jù)等,這些情況往往需要用戶做出相應(yīng)操作。
3.1 AT 組件簡介
AT 組件是基于 RT-Thread 系統(tǒng)的 AT Server 和 AT Client 的實現(xiàn),組件完成 AT 命令的發(fā)送、命令格式及參數(shù)判斷、命令的響應(yīng)、響應(yīng)數(shù)據(jù)的接收、響應(yīng)數(shù)據(jù)的解析、URC 數(shù)據(jù)處理等整個 AT 命令數(shù)據(jù)交互流程。
通過 AT 組件,設(shè)備可以作為 AT Client 使用串口連接其他設(shè)備發(fā)送并接收解析數(shù)據(jù),可以作為 AT Server 讓其他設(shè)備甚至電腦端連接完成發(fā)送數(shù)據(jù)的響應(yīng),也可以在本地 shell 啟動 CLI 模式使設(shè)備同時支持 AT Server 和 AT Client 功能,該模式多用于設(shè)備開發(fā)調(diào)試。
AT Server 主要功能特點:
基礎(chǔ)命令:實現(xiàn)多種通用基礎(chǔ)命令(ATE、ATZ 等);
命令兼容:命令支持忽略大小寫,提高命令兼容性;
命令檢測:命令支持自定義參數(shù)表達式,并實現(xiàn)對接收的命令參數(shù)自檢測功能;
命令注冊:提供簡單的用戶自定義命令添加方式,類似于 finsh/msh 命令添加方式;
調(diào)試模式:提供 AT Server CLI 命令行交互模式,主要用于設(shè)備調(diào)試。
AT Client 主要功能特點:
URC 數(shù)據(jù)處理:完備的 URC 數(shù)據(jù)的處理方式;
數(shù)據(jù)解析:支持自定義響應(yīng)數(shù)據(jù)的解析方式,方便獲取響應(yīng)數(shù)據(jù)中相關(guān)信息;
調(diào)試模式:提供 AT Client CLI 命令行交互模式,主要用于設(shè)備調(diào)試。
AT Socket:作為 AT Client 功能的延伸,使用 AT 命令收發(fā)作為基礎(chǔ),實現(xiàn)標準的 BSD Socket API,完成數(shù)據(jù)的收發(fā)功能,使用戶通過 AT 命令完成設(shè)備連網(wǎng)和數(shù)據(jù)通訊。
多客戶端支持:AT 組件目前支持多客戶端同時運行
3.2 AT Client
AT Client 主要功能是發(fā)送 AT 命令、接收數(shù)據(jù)并解析數(shù)據(jù)。
AT Client列表:
1static struct at_client at_client_table[AT_CLIENT_NUM_MAX] = { 0 };
AT 客戶端都掛載在at_client_table里。
AT Client數(shù)據(jù)結(jié)構(gòu):
3.2.1 AT Client 初始化
創(chuàng)建AT客戶端對象,初始化客戶端對象參數(shù)。
1int at_client_init(const char *dev_name, rt_size_t recv_bufsz);
at_client_init() 函數(shù)完成對 AT Client 設(shè)備初始化、AT Client 移植函數(shù)的初始化、AT Client 使用的信號量、互斥鎖等資源初始化,并創(chuàng)建 at_client 線程用于 AT Client 中數(shù)據(jù)的接收的解析以及對 URC 數(shù)據(jù)的處理。
3.2.2 AT Client 數(shù)據(jù)收發(fā)方式
創(chuàng)建響應(yīng)結(jié)構(gòu)體:
1at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
刪除響應(yīng)結(jié)構(gòu)體:
1void at_delete_resp(at_response_t resp);
設(shè)置響應(yīng)結(jié)構(gòu)體參數(shù):
1at_response_t at_resp_set_info(at_response_t resp, rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
發(fā)送命令并接收響應(yīng):
1 rt_err_t at_exec_cmd(at_response_t resp, const char *cmd_expr, 。..);
3.2.3 AT Client 數(shù)據(jù)解析方式
獲取指定行號的響應(yīng)數(shù)據(jù):
該函數(shù)用于在 AT Server 響應(yīng)數(shù)據(jù)中獲取指定行號的一行數(shù)據(jù)。
1const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line);
獲取指定關(guān)鍵字的響應(yīng)數(shù)據(jù):
該函數(shù)用于在 AT Server 響應(yīng)數(shù)據(jù)中通過關(guān)鍵字獲取對應(yīng)的一行數(shù)據(jù)。
1const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword);
解析指定行號的響應(yīng)數(shù)據(jù):
該函數(shù)用于在 AT Server 響應(yīng)數(shù)據(jù)中獲取指定行號的一行數(shù)據(jù), 并解析該行數(shù)據(jù)中的參數(shù)。
1int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, 。..);
發(fā)送命令并解析接收響應(yīng)例程:
1/*
2 * 程序清單:AT Client 發(fā)送命令并解析接收響應(yīng)例程
3 */
4
5int user_at_client_send(int argc, char**argv)
6{
7 at_response_t resp = RT_NULL;
8 char ip[20];
9 char mac[20];
10 char uartdata[20];
11 if (argc != 2)
12 {
13 LOG_E(“at_cli_send [command] - AT client send commands to AT server.”);
14 return -RT_ERROR;
15 }
16
17 /* 創(chuàng)建響應(yīng)結(jié)構(gòu)體,設(shè)置最大支持響應(yīng)數(shù)據(jù)長度為 512 字節(jié),響應(yīng)數(shù)據(jù)行數(shù)無限制,超時時間為 5 秒 */
18 resp = at_create_resp(512, 0, rt_tick_from_millisecond(5000));
19 if (!resp)
20 {
21 LOG_E(“No memory for response structure!”);
22 return -RT_ENOMEM;
23 }
24
25 /* 發(fā)送 AT 命令并接收 AT Server 響應(yīng)數(shù)據(jù),數(shù)據(jù)及信息存放在 resp 結(jié)構(gòu)體中 */
26 if (at_exec_cmd(resp, argv[1]) != RT_EOK)
27 {
28 LOG_E(“AT client send commands failed, response error or timeout !”);
29 return -1;
30 }
31
32 /* 命令發(fā)送成功 */
33 rt_kprintf(“AT Client send commands to AT Server success!
”);
34 if(at_resp_get_line_by_kw(resp,“UART”)!= NULL)
35 {
36 /* 解析獲取串口配置信息AT+UART?,1 表示解析響應(yīng)數(shù)據(jù)第一行 */
37 at_resp_parse_line_args(resp, 1,“+UART:%s”, uartdata);
38 rt_kprintf(“+UART:%s
”,uartdata);
39 }
40 /* 刪除響應(yīng)結(jié)構(gòu)體 */
41 at_delete_resp(resp);
42
43 return RT_EOK;
44}
45/* 輸出 at_Client_send 函數(shù)到 msh 中 */
46MSH_CMD_EXPORT(user_at_client_send, AT Client send commands to AT Server and get response data);
3.2.4 AT Client URC數(shù)據(jù)處理
URC 數(shù)據(jù)的處理是 AT Client 另一個重要功能,URC 數(shù)據(jù)為服務(wù)器主動下發(fā)的數(shù)據(jù),不能通過上述數(shù)據(jù)發(fā)送接收函數(shù)接收,并且對于不同設(shè)備 URC 數(shù)據(jù)格式和功能不一樣,所以 URC 數(shù)據(jù)處理的方式也是需要用戶自定義實現(xiàn)的。
每種 URC 數(shù)據(jù)都有一個結(jié)構(gòu)體控制塊,用于定義判斷 URC 數(shù)據(jù)的前綴和后綴,以及 URC 數(shù)據(jù)的執(zhí)行函數(shù)。一段數(shù)據(jù)只有完全匹配 URC 的前綴和后綴才能定義為 URC 數(shù)據(jù),獲取到匹配的 URC 數(shù)據(jù)后會立刻執(zhí)行 URC 數(shù)據(jù)執(zhí)行函數(shù)。所以開發(fā)者添加一個 URC 數(shù)據(jù)需要自定義匹配的前綴、后綴和執(zhí)行函數(shù)。
URC 數(shù)據(jù)列表初始化:
1void at_set_urc_table(const struct at_urc *table, rt_size_t size);
AT Client 移植具體示例:
1static void urc_conn_func(const char *data, rt_size_t size)
2{
3 /* WIFI 連接成功信息 */
4 LOG_D(“AT Server device WIFI connect success!”);
5}
6
7static void urc_recv_func(const char *data, rt_size_t size)
8{
9 /* 接收到服務(wù)器發(fā)送數(shù)據(jù) */
10 LOG_D(“AT Client receive AT Server data!”);
11}
12
13static void urc_func(const char *data, rt_size_t size)
14{
15 /* 設(shè)備啟動信息 */
16 LOG_D(“AT Server device startup!”);
17}
18
19static struct at_urc urc_table[] = {
20 {“WIFI CONNECTED”, “
”, urc_conn_func},
21 {“+RECV”, “:”, urc_recv_func},
22 {“RDY”, “
”, urc_func},
23};
24
25int at_client_port_init(void)
26{
27 /* 添加多種 URC 數(shù)據(jù)至 URC 列表中,當接收到同時匹配 URC 前綴和后綴的數(shù)據(jù),執(zhí)行 URC 函數(shù) */
28 at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
29 return RT_EOK;
30}
3.2.5 AT Client其他接口
發(fā)送指定長度數(shù)據(jù):
1rt_size_t at_client_send(const char *buf, rt_size_t size);
接收指定長度數(shù)據(jù):
1rt_size_t at_client_recv(char *buf, rt_size_t size,rt_int32_t timeout);
設(shè)置接收數(shù)據(jù)的行結(jié)束符:
1void at_set_end_sign(char ch);
等待模塊初始化完成:
1int at_client_wait_connect(rt_uint32_t timeout);
3.3 AT 協(xié)議簇
3.3.1 AT 設(shè)備框架
網(wǎng)卡的初始化和注冊建立在協(xié)議簇類型上,所以每種網(wǎng)卡對應(yīng)唯一的協(xié)議簇類型。每種協(xié)議棧對應(yīng)一種協(xié)議簇類型(family),AT協(xié)議簇對應(yīng)的協(xié)議棧是AT Socket 協(xié)議棧,每種AT設(shè)備都對應(yīng)唯一的AT Socket 協(xié)議棧。
AT 設(shè)備列表:
1/* The global list of at device */
2static rt_slist_t at_device_list = RT_SLIST_OBJECT_INIT(at_device_list);
3/* The global list of at device class */
4static rt_slist_t at_device_class_list = RT_SLIST_OBJECT_INIT(at_device_class_list);
at設(shè)備的具體網(wǎng)卡對象,例如(esp8266網(wǎng)卡、esp32網(wǎng)卡等)注冊到at_device_class_list 列表,對at_device_class_list 創(chuàng)建的網(wǎng)卡對象進行填充。網(wǎng)卡注冊在驅(qū)動層進行。
at設(shè)備對象注冊到at_device_list列表,對at設(shè)備的具體網(wǎng)卡對象進行統(tǒng)一管理。AT設(shè)備注冊在應(yīng)用層進行。
AT設(shè)備數(shù)據(jù)結(jié)構(gòu):
AT設(shè)備注冊接口:
1int at_device_register(struct at_device *device, const char *device_name,
2 const char *at_client_name, uint16_t class_id, void *user_data)
應(yīng)用層運行AT設(shè)備注冊接口之前,需要先在外設(shè)驅(qū)動相關(guān)的自動初始化機制INIT_DEVICE_EXPORT(fn) 申明注冊AT類的網(wǎng)卡設(shè)備,然后應(yīng)用層注冊AT設(shè)備的時候才能在at_device_class_list 列表里通過AT設(shè)備ID找到具體的網(wǎng)卡驅(qū)動。
3.3.2 AT Socket
AT Socket 是AT Client 功能的延伸,使用 AT 命令收發(fā)作為基礎(chǔ)功能,提供 ping 或者 ifconfig等命令用于測試設(shè)備網(wǎng)絡(luò)連接環(huán)境,ping 命令原理是通過 AT 命令發(fā)送請求到服務(wù)器,服務(wù)器響應(yīng)數(shù)據(jù),客戶端解析 ping 數(shù)據(jù)并顯示。ifocnfig 命令可以查看當前設(shè)備網(wǎng)絡(luò)狀態(tài)和 AT 設(shè)備生成的網(wǎng)卡基本信息。
AT Socket 功能的使用依賴于如下幾個組件:
AT 組件:AT Socket 功能基于 AT Client 功能的實現(xiàn);
SAL 組件:SAL 組件主要是 AT Socket 接口的抽象,實現(xiàn)標準 BSD Socket API;
netdev 組件:用于抽象和管理 AT 設(shè)備生成的網(wǎng)卡設(shè)備相關(guān)信息,提供 ping、ifconfig、netstat 等網(wǎng)絡(luò)命令;
AT Device 軟件包:針對不同設(shè)備的 AT Socket 移植和示例文件,以軟件包的形式給出;
AT Socket 數(shù)據(jù)結(jié)構(gòu):
3.3.2.1 AT Socket API介紹
1int at_socket(int domain, int type, int protocol)
通過協(xié)議族AF_AT獲取第一個指定協(xié)議簇類型的網(wǎng)卡對象
通過網(wǎng)卡對象的名字獲得AT設(shè)備的對象
通過AT設(shè)備的對象分配并初始化一個新的AT套接字
1int at_bind(int socket, const struct sockaddr *name, socklen_t namelen)
獲取當前設(shè)備ip地址
從sockaddr結(jié)構(gòu)中選擇ip地址和端口
如果輸入的ip地址不同于設(shè)備的ip地址,則根據(jù)輸入的ip分配新的套接字,否則返回。
1int at_connect(int socket, const struct sockaddr *name, socklen_t namelen)
socketaddr結(jié)構(gòu)獲取IP地址和端口
調(diào)用對應(yīng)AT網(wǎng)卡驅(qū)動的_socket_connect()鏈接服務(wù)器
設(shè)置套接字接收數(shù)據(jù)回調(diào)函數(shù)
1int at_sendto(int socket, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen)
調(diào)用對應(yīng)AT網(wǎng)卡驅(qū)動的_socket_send()發(fā)送數(shù)據(jù)
其他API
1int at_closesocket(int socket)
2
1int at_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
2
1int at_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen)
2
1int at_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)
2
1int at_shutdown(int socket, int how)
2
4、應(yīng)用實例
4.1 使用at_device軟件包的ESP8266模組
使用AT Socket 功能的框架:
啟動流程:
4.1.1 注冊ESP8266設(shè)備驅(qū)動
1static int esp8266_device_class_register(void)
2
創(chuàng)建并初始化ESP8266 device class對象
在at_device_class_list列表注冊AT_DEVICE_CLASS_ESP8266客戶端ID
注冊esp8266設(shè)備操作函數(shù):
1static const struct at_device_ops esp8266_device_ops =
2{
3 esp8266_init,
4 esp8266_deinit,
5 esp8266_control,
6};
7class-》device_ops = &esp8266_device_ops
8
注冊esp8266_at_socket操作接口:
1static const struct at_socket_ops esp8266_socket_ops =
2{
3 esp8266_socket_connect,
4 esp8266_socket_close,
5 esp8266_socket_send,
6 esp8266_domain_resolve,
7 esp8266_socket_set_event_cb,
8};
4.1.2 初始化ESP8266設(shè)備,鏈接上無線網(wǎng)絡(luò)
1#define ESP8266_SAMPLE_DEIVCE_NAME “esp0”
2static struct at_device_esp8266 esp0 =
3{
4 ESP8266_SAMPLE_DEIVCE_NAME,
5 ESP8266_SAMPLE_CLIENT_NAME,
6
7 ESP8266_SAMPLE_WIFI_SSID,
8 ESP8266_SAMPLE_WIFI_PASSWORD,
9 ESP8266_SAMPLE_RECV_BUFF_LEN,
10};
11
12struct at_device_esp8266 *esp8266 = &esp0;
13
14return at_device_register(&(esp8266-》device),
15 esp8266-》device_name,
16 esp8266-》client_name,
17 AT_DEVICE_CLASS_ESP8266,
18 (void *) esp8266);
從at_device_class_list列表通過客戶端ID獲取ESP8266設(shè)備類對象
創(chuàng)建并初始化AT device class對象
在at_device_list列表注冊AT設(shè)備
調(diào)用ESP8266設(shè)備類對象的初始化驅(qū)動接口
1static int esp8266_init(struct at_device *device)
創(chuàng)建esp_net線程,鏈接無線網(wǎng)絡(luò)后自動銷毀
1static void esp8266_init_thread_entry(void *parameter)
注冊ESP8266設(shè)備操作接口:
1static const struct netdev_ops esp8266_netdev_ops =
2{
3 esp8266_netdev_set_up,
4 esp8266_netdev_set_down,
5
6 esp8266_netdev_set_addr_info,
7 esp8266_netdev_set_dns_server,
8 esp8266_netdev_set_dhcp,
9
10#ifdef NETDEV_USING_PING
11 esp8266_netdev_ping,
12#endif
13#ifdef NETDEV_USING_NETSTAT
14 esp8266_netdev_netstat,
15#endif
16};
17netdev-》ops = &esp8266_netdev_ops
1static int esp8266_net_init(struct at_device *device)
2
注冊urc_table
1static const struct at_urc urc_table[] =
2{
3 {“busy p”, “
”, urc_busy_p_func},
4 {“busy s”, “
”, urc_busy_s_func},
5 {“WIFI CONNECTED”, “
”, urc_func},
6 {“WIFI DISCONNECT”, “
”, urc_func},
7};
1static const struct at_urc urc_table[] =
2{
3 {“SEND OK”, “
”, urc_send_func},
4 {“SEND FAIL”, “
”, urc_send_func},
5 {“Recv”, “bytes
”, urc_send_bfsz_func},
6 {“”, “,CLOSED
”, urc_close_func},
7 {“+IPD”, “:”, urc_recv_func},
8};
4.2 lwip網(wǎng)絡(luò)協(xié)議棧驅(qū)動移植
驅(qū)動架構(gòu)圖:
4.2.1 添加lwip協(xié)議棧軟件包
在 RT-Thread Setting 文件中借助圖形化配置工具打開軟件 lwip 的組件,保存更新。
4.2.2 移植網(wǎng)絡(luò)設(shè)備層和LAN8720驅(qū)動移植
移植網(wǎng)絡(luò)設(shè)備層和LAN8720驅(qū)動:
本例中使用的是 stm32f429-fire-challenger開發(fā)板,所以需要下載 BSP的LWIP驅(qū)動,將下載的LWIP驅(qū)動源碼 drv_etc.c 和 drv_etc.h 文件添加到自己工程驅(qū)動文件所在的路徑。
將drv_etc.c代碼做一下更動:
將#include 《drv_log.h》改為#include 《rtdbg.h》
刪除extern void phy_reset(void);和 phy_reset();
添加ETH外設(shè)配置:
打開stm32f429-fire-challenger的BSP,在board目錄下找到stm32f4xx_hal_msp.c文件,移植到工程中。
然后改動stm32f4xx_hal_msp.c里的代碼:
把#include “main.h”改為#include “board.h”
刪除多余的配置,只保留void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)和void HAL_ETH_MspDeInit(ETH_HandleTypeDef* heth)
打開include “board.h”,添加#define PHY_USING_LAN8720A
移植完成,編譯。
4.2.3 網(wǎng)絡(luò)設(shè)備層和LAN8720驅(qū)動解析
4.2.3.1 網(wǎng)絡(luò)設(shè)備層解析
RT-Thread 的 lwIP 移植在原版的基礎(chǔ)上,添加了網(wǎng)絡(luò)設(shè)備層以替換原來的驅(qū)動層。和原來的驅(qū)動層不同的是,對于以太網(wǎng)數(shù)據(jù)的收發(fā)采用了獨立的雙線程結(jié)構(gòu),erx 線程和 etx 線程在正常情況下,兩者的優(yōu)先級設(shè)置成相同,用戶可以根據(jù)自身實際要求進行微調(diào)以側(cè)重接收或發(fā)送。
數(shù)據(jù)接收流程:
當以太網(wǎng)硬件設(shè)備收到網(wǎng)絡(luò)報文產(chǎn)生中斷時,接收到的數(shù)據(jù)會被存放到接收緩沖區(qū),然后以太網(wǎng)中斷程序會發(fā)送郵件來喚醒 erx 線程,erx 線程會按照接收到的數(shù)據(jù)長度來申請 pbuf,并將數(shù)據(jù)放入 pbuf 的 payload 中,然后將 pbuf 通過郵件發(fā)送給 去處理。
數(shù)據(jù)發(fā)送流程:
當有數(shù)據(jù)需要發(fā)送時,LwIP 會將數(shù)據(jù)通過郵件發(fā)送給 etx 線程,然后永久等待在 tx_ack 信號量上。etx 線程接收到郵件后,通過調(diào)用驅(qū)動中的 rt_stm32_eth_tx() 函數(shù)發(fā)送數(shù)據(jù),發(fā)送完成之后再發(fā)送一次 tx_ack 信號量喚醒 LwIP
網(wǎng)絡(luò)設(shè)備介紹:
RT-Thread 網(wǎng)絡(luò)設(shè)備繼承了標準設(shè)備,由 eth_device 結(jié)構(gòu)體定義:
1struct eth_device
2{
3 /* 標準設(shè)備 */
4 struct rt_device parent;
5
6 /* lwIP 網(wǎng)絡(luò)接口 */
7 struct netif *netif;
8 /* 發(fā)送應(yīng)答信號量 */
9 struct rt_semaphore tx_ack;
10
11 /* 網(wǎng)絡(luò)狀態(tài)標志 */
12 rt_uint16_t flags;
13 rt_uint8_t link_changed;
14 rt_uint8_t link_status;
15
16 /* 數(shù)據(jù)包收發(fā)接口 */
17 struct pbuf* (*eth_rx)(rt_device_t dev);
18 rt_err_t (*eth_tx)(rt_device_t dev, struct pbuf* p);
19};
實現(xiàn)數(shù)據(jù)包收發(fā)接口,對應(yīng)了 eth_device 結(jié)構(gòu)體中的 eth_rx 及 eth_tx 元素:
1rt_err_t rt_stm32_eth_tx(rt_device_t dev, struct pbuf* p);
2struct pbuf *rt_stm32_eth_rx(rt_device_t dev);
注冊以太網(wǎng)設(shè)備,初始化以太網(wǎng)硬件,配置 MAC 地址:
1rt_err_t eth_device_init_with_flag(struct eth_device *dev, const char *name, rt_uint16_t flags)
此函數(shù)由LAN8720的驅(qū)動rt_hw_stm32_eth_init()調(diào)用。
4.2.3.2 LAN8720驅(qū)動解析:
LAN8720網(wǎng)卡對象stm32_eth_device由rt_stm32_eth類創(chuàng)建,rt_stm32_eth類繼承自eth_device類。
rt_stm32_eth的結(jié)構(gòu)定義:
1struct rt_stm32_eth
2{
3 /* inherit from ethernet device */
4 struct eth_device parent;
5 rt_timer_t poll_link_timer;
6 /* interface address info, hw address */
7 rt_uint8_t dev_addr[MAX_ADDR_LEN];
8 /* ETH_Speed */
9 uint32_t ETH_Speed;
10 /* ETH_Duplex_Mode */
11 uint32_t ETH_Mode;
12};
實現(xiàn)rt_device設(shè)備的接口:
1static rt_err_t rt_stm32_eth_init(rt_device_t dev);
2static rt_err_t rt_stm32_eth_open(rt_device_t dev, rt_uint16_t oflag);
3static rt_err_t rt_stm32_eth_close(rt_device_t dev);
4static rt_size_t rt_stm32_eth_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
5static rt_size_t rt_stm32_eth_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
6static rt_err_t rt_stm32_eth_control(rt_device_t dev, int cmd, void *args);
rt_stm32_eth_init 用于初始化 DMA 和 MAC 控制器。
rt_stm32_eth_open 用于上層應(yīng)用打開網(wǎng)絡(luò)設(shè)備,目前未使用到,直接返回 RT_EOK。
rt_stm32_eth_close 用于上層應(yīng)用關(guān)閉網(wǎng)絡(luò)設(shè)備,目前未使用到,直接返回 RT_EOK。
rt_stm32_eth_read 用于上層應(yīng)用向底層設(shè)備進行直接讀寫的情況,對于網(wǎng)絡(luò)設(shè)備,每個報文都有固定的格式,所以這個接口目前并未使用,直接返回 0 值。
rt_stm32_eth_write 用于上層應(yīng)用向底層設(shè)備進行直接讀寫的情況,對于網(wǎng)絡(luò)設(shè)備,每個報文都有固定的格式,所以這個接口目前并未使用,直接返回 0 值。
rt_stm32_eth_control 用于控制以太網(wǎng)接口設(shè)備,目前用于獲取以太網(wǎng)接口的 mac 地址。如果需要,也可以通過增加控制字的方式來擴展其他控制功能。
實現(xiàn)驅(qū)動層的數(shù)據(jù)包收發(fā)接口:
1rt_stm32_eth_rx()
rt_stm32_eth_rx 會去讀取接收緩沖區(qū)中的數(shù)據(jù),并放入 pbuf(lwIP 中利用結(jié)構(gòu)體 pbuf 來管理數(shù)據(jù)包 )中,并返回 pbuf 指針。
網(wǎng)絡(luò)設(shè)備層的“erx” 接收線程會阻塞在獲取 eth_rx_thread_mb 郵箱上,當它接收到郵件時,會調(diào)用 rt_stm32_eth_rx 去接收數(shù)據(jù)。
1rt_stm32_eth_tx()
rt_stm32_eth_tx 會將要發(fā)送的數(shù)據(jù)放入發(fā)送緩沖區(qū),等待 DMA 來發(fā)送數(shù)據(jù)。
網(wǎng)絡(luò)設(shè)備層的“etx” 發(fā)送線程會阻塞在獲取 eth_tx_thread_mb 郵箱上, 當它接收到郵件時,會調(diào)用 rt_stm32_eth_tx 來發(fā)送數(shù)據(jù)。
ETH 設(shè)備初始化:
1static int rt_hw_stm32_eth_init(void)
2INIT_DEVICE_EXPORT(rt_hw_stm32_eth_init);
由系統(tǒng)自動初始化機制調(diào)用。
lwip協(xié)議棧初始化:
1int lwip_system_init(void)
2INIT_PREV_EXPORT(lwip_system_init)
由系統(tǒng)自動初始化機制調(diào)用。
5 使用網(wǎng)卡設(shè)備鏈接服務(wù)器
下面示例完成通過傳入的網(wǎng)卡名稱綁定該網(wǎng)卡 IP 地址并和服務(wù)器進行連接通信的過程:
1static int bing_test(int argc, char **argv)
2{
3 struct sockaddr_in client_addr;
4 struct sockaddr_in server_addr;
5 struct netdev *netdev = RT_NULL;
6 int sockfd = -1;
7 int AF = -1;
8 uint8_t send_buf[]= “This is a TCP Client test.。.
”;
9 uint8_t read_buf[10];
10 if (argc != 2)
11 {
12 rt_kprintf(“bind_test [netdev_name] --bind network interface device by name.
”);
13 return -RT_ERROR;
14 }
15 if(rt_strcmp(argv[1], “esp0”) == 0)
16 {
17 AF = AF_AT;
18 }else if(rt_strcmp(argv[1], “e0”) == 0){
19 AF = AF_INET;
20 }else{
21 return -RT_ERROR;
22 }
23 /* 通過名稱獲取 netdev 網(wǎng)卡對象 */
24 netdev = netdev_get_by_name(argv[1]);
25 if (netdev == RT_NULL)
26 {
27 rt_kprintf(“get network interface device(%s) failed.
”, argv[1]);
28 return -RT_ERROR;
29 }
30 /* 設(shè)置默認網(wǎng)卡對象 */
31 netdev_set_default(netdev);
32 if ((sockfd = socket(AF, SOCK_STREAM, 0)) 《 0)
33 {
34 rt_kprintf(“Socket create failed.
”);
35 return -RT_ERROR;
36 }
37
38 /* 初始化需要綁定的客戶端地址 */
39 client_addr.sin_family = AF;
40 client_addr.sin_port = htons(8080);
41 /* 獲取網(wǎng)卡對象中 IP 地址信息 */
42 client_addr.sin_addr.s_addr = netdev-》ip_addr.addr;
43 rt_memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));
44
45 if (bind(sockfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) 《 0)
46 {
47 rt_kprintf(“socket bind failed.
”);
48 closesocket(sockfd);
49 return -RT_ERROR;
50 }
51 rt_kprintf(“socket bind network interface device(%s) success!
”, netdev-》name);
52
53 /* 初始化預(yù)連接的服務(wù)端地址 */
54 server_addr.sin_family = AF;
55 server_addr.sin_port = htons(SERVER_PORT);
56 server_addr.sin_addr.s_addr = inet_addr(SERVER_HOST);
57 rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
58
59 /* 連接到服務(wù)端 */
60 if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) 《 0)
61 {
62 rt_kprintf(“socket connect failed!
”);
63 closesocket(sockfd);
64 return -RT_ERROR;
65 }
66 else
67 {
68 rt_kprintf(“socket connect success!
”);
69 }
70 write(sockfd,send_buf,sizeof(send_buf));
71 read(sockfd,read_buf,sizeof(read_buf));
72 rt_kprintf(“%s
”,read_buf);
73 /* 關(guān)閉連接 */
74 closesocket(sockfd);
75 return RT_EOK;
76}
77MSH_CMD_EXPORT(bing_test, bind network interface device test);
? ? ? ?責(zé)任編輯:pj
評論