在進行本節之前,首先解決大家的一個疑惑點:Client和Client_Socket有什么區別或分別代表的含義?
Socket標準定義為套接字,應用于主流的網絡設計程序,具有使用簡單,多平臺移植方便的特點。在Socket應用中,使用一個套接字來記錄網絡的一個連接,套接字是一個整數,就像操作文件一樣,利用一個文件描述符,進行打開、讀、寫、關閉等操作。在網絡中,可以對Socket 套接字進行類似的操作,比如開啟一個網絡的連接、讀取連接主機發送來的數據、向連接的主機發送數據、終止連接等操作。LwIP設計目的主要應用于嵌入式平臺,對于Socket的支持并不完全,只是通過對netconn進行封裝實現部分功能,使得LwIP也具有多平臺應用的特性,通過Socket方式的了解能夠極大簡化通信過程的理解,快速實現應用開發。
Demo應用中,使用的開發板為MB-039,在工程中使用LwIP+FreeRTOS,實驗展示如何制作一個客戶端并發送數據,板載Ethernet相關的硬件部分電路如下:
各個信號引腳對應如下:
通過配置復用相關引腳為RMII相關的功能,初始化以太網功能,執行FreeRTOS的啟動。具體過程可參考樣例初始化程序中代碼。
在進行Client_Socket實驗前,我們先了解需要使用到的應用功能函數:
(1)socket () (2)connect () (3)write ()
(1) socket ()
Socket()指向lwip_socket(),功能為申請一個套接字,lwip_socket()源碼如下:
int lwip_socket(int domain, int type, int protocol) { struct netconn *conn; int i; LWIP_UNUSED_ARG(domain); /* @todo: check this */ /* create a netconn */ switch (type) { case SOCK_RAW: conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW), (u8_t)protocol, DEFAULT_SOCKET_EVENTCB); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; case SOCK_DGRAM: conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)), DEFAULT_SOCKET_EVENTCB); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); #if LWIP_NETBUF_RECVINFO if (conn) { /* netconn layer enables pktinfo by default, sockets default to off */ conn->flags = ~NETCONN_FLAG_PKTINFO; } #endif /* LWIP_NETBUF_RECVINFO */ break; case SOCK_STREAM: conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); break; default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1n", domain, type, protocol)); set_errno(EINVAL); return -1; } if (!conn) { LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)n")); set_errno(ENOBUFS); return -1; } i = alloc_socket(conn, 0); if (i == -1) { netconn_delete(conn); set_errno(ENFILE); return -1; } conn->socket = i; done_socket( sockets[i - LWIP_SOCKET_OFFSET]); LWIP_DEBUGF(SOCKETS_DEBUG, ("%dn", i)); set_errno(0); return i; }
從源碼中我們可以看出,本質上是對netconn_new()進行封裝。我們關注一下其參數,domain表示協議簇,對于IP/TCP來說該值始終為AF_INET。重點需要關注一下type,我們查看API手冊對于幾種類型的解釋如下:
1. SOCK_STREAM:提供可靠的(即能保證數據正確傳送到對方)面向連接Socket服務,多用于資料(如文件)傳輸,如TCP協議。
2. SOCK_DGRAM:是提供無保障的面向消息的Socket服務,主要用于在網絡上發廣播信息,如UDP協議,提供無連接不可靠的數據報交付服務。
3. SOCK_RAW:表示原始套接字,它允許應用程序訪問網絡層的原始數據包,這個套接字用得比較少,暫時不用理會它。
Protocol指定套接字使用的協議,對于IPv4,TCP協議提供SOCK_STREAM服務,只有UDP協議提供SOCK_DGRAM服務。
(2) connect ()
connect()指向lwip_connect()(源碼較長,就不進行粘貼了),函數的作用與前文介紹netconn_connect功能一致,通過源碼可以知道其是通過對netconn_connect的封裝實現。在TCP客戶端連接中,調用這個函數將發生握手過程,并最終建立新的TCP連接。對于UDP來說調用這個函數只是在UDP控制塊中記錄遠端IP地址與端口號。
(3) write ()
Write()指向lwip_write,源碼如下,其通過調用lwip_send實現,flags為0。
ssize_t lwip_write(int s, const void *data, size_t size) { return lwip_send(s, data, size, 0); }
了解了以上3個API,接下來開始創建Client_Socket工程:
static void client(void *thread_param) { int sock = -1; struct sockaddr_in client_addr; ip4_addr_t ipaddr; uint8_t send_buf[]= " This is MM32F3270 TCP Client_Socket Demo n"; IP4_ADDR( ipaddr,DEST_IP_ADDR0,DEST_IP_ADDR1,DEST_IP_ADDR2,DEST_IP_ADDR3); while(1) { sock = socket(AF_INET, SOCK_STREAM, 0); //(1) if (sock < 0) { vTaskDelay(10); continue; } client_addr.sin_family = AF_INET; //(2) client_addr.sin_port = htons(DEST_PORT); //(3) client_addr.sin_addr.s_addr = ipaddr.addr; //(4) memset( (client_addr.sin_zero), 0, sizeof(client_addr.sin_zero)); if (connect(sock, (struct sockaddr *) client_addr, sizeof(struct sockaddr)) == -1) //(5) { printf("Connect failed!n"); closesocket(sock); vTaskDelay(10); continue; } while (1) { if(write(sock,send_buf,sizeof(send_buf)) < 0) //(6) break; vTaskDelay(1000); } closesocket(sock); } }
(1)申請一個套接字:socket
(2)協議簇類型(AF_INET用于TCP/IP協議)
(3)將端口賦值給client_addr的sin_port成員
(4)將地址賦值給client_addr的sin_addr.s_addr成員
(5)創建連接,將sock與地址端口進行綁定,建立連接
(6)發送數據
到這里已經完成了Client Socket工程的創建,還有一步比較重要的是配置Client與Server端的IP,將數據發送給服務器端。
在Windows下,通過打開命令行窗口輸入:ipconfig可以獲取本機地址與服務器的地址。
可以觀察到PC地址為:192.168.105.34,在sys_arch.h文件中對DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3進行修改,DEST_PORT可選用空閑端口,設備IP需要設置在同一個網段內通信才能進行IP_ADDR0、IP_ADDR1 、IP_ADDR2,需要與PC地址保持一致,IP_ADDR3可以隨意設置(和PC地址不一致即可)。
#define DEST_IP_ADDR0 192 #define DEST_IP_ADDR1 168 #define DEST_IP_ADDR2 105 #define DEST_IP_ADDR3 34 #define DEST_PORT 5001 #define IP_ADDR0 192 #define IP_ADDR1 168 #define IP_ADDR2 105 #define IP_ADDR3 130
將程序下載入開發板中,使用SSCOM工具進行如下設置:
點擊偵聽:
可以觀察到正常偵聽并接收到數據,表明實驗成功。Demo程序可登錄MindMotion的官網下載MM32F3270 lib_Samples。
來源:靈動MM32MCU
審核編輯:湯梓紅
-
嵌入式
+關注
關注
5092文章
19177瀏覽量
307663 -
以太網
+關注
關注
40文章
5460瀏覽量
172726 -
開發板
+關注
關注
25文章
5121瀏覽量
98193 -
FreeRTOS
+關注
關注
12文章
484瀏覽量
62395
發布評論請先 登錄
相關推薦
靈動微課堂 (第182講) | 基于MM32F3270 以太網 Client_Socket使用
靈動微課堂 (第183講) | 基于MM32F3270 以太網 Server_Socket使用
靈動微課堂 (第185講) | 基于MM32F3270 以太網 Client使用
MM32F3270系列32位MCU的特點有哪些
基于MM32F3270 以太網 Client使用
![基于<b class='flag-5'>MM32F3270</b> <b class='flag-5'>以太網</b> <b class='flag-5'>Client</b>使用](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
基于MM32F3270 以太網 Server_Socket使用
![基于<b class='flag-5'>MM32F3270</b> <b class='flag-5'>以太網</b> Server_<b class='flag-5'>Socket</b>使用](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論