diff --git a/applications/app/BUILD.gn b/applications/app/BUILD.gn index a14d29429ebd98b10569cafb9cf3cf2eab955447..aa793d3abdd616bd3d8777823f93cdb0ea67d2b8 100644 --- a/applications/app/BUILD.gn +++ b/applications/app/BUILD.gn @@ -15,7 +15,7 @@ import("//build/lite/config/component/lite_component.gni") lite_component("app") { features = [ - "TW001_OS_helloworld:helloworld", + # "TW001_OS_helloworld:helloworld", # "TW002_OS_thread:os_thread_example", # "TW003_OS_timer:os_timer_example", # "TW004_OS_event:os_event_example", @@ -40,5 +40,9 @@ lite_component("app") { # "TW302_Network_wifiap:network_wifiap_example", # "TW303_Network_mqttclient:network_mqttclient_example", # "TW402_APP_oled_u8g2:app_oled_u8g2_example", + # "TW304_Network_tcpserver:network_tcpserver_demo", + # "TW304_Network_tcpclient:network_tcpclient_demo", + "TW305_Network_udpclient:network_udpclient_demo", + # "TW305_Network_udpserver:network_udpserver_demo", ] } diff --git a/applications/app/TW304_Network_tcpclient/BUILD.gn b/applications/app/TW304_Network_tcpclient/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..296e785645bd57704e5922d7e3eed25dcc53ad58 --- /dev/null +++ b/applications/app/TW304_Network_tcpclient/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Talkweb Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +static_library("network_tcpclient_demo") { + sources = [ + "src/wifi_connect.c", + "src/tcp_client.c", + ] + + cflags = [ "-Wno-unused-variable" ] + cflags += [ "-Wno-unused-but-set-variable" ] + + include_dirs = [ + "//foundation/communication/wifi_lite/interfaces/wifiservice" , + "include" + ] +} \ No newline at end of file diff --git a/applications/app/TW304_Network_tcpclient/README.md b/applications/app/TW304_Network_tcpclient/README.md new file mode 100644 index 0000000000000000000000000000000000000000..13742caa89a84cc808d6cf9125f730379d2b612f --- /dev/null +++ b/applications/app/TW304_Network_tcpclient/README.md @@ -0,0 +1,305 @@ +# Niobe开发板TCP联网演示 + +本案例程序将演示怎么在拓维Niobe WiFi IoT Core开发板上编写一个连接tcp服务器的业务程序,实现开发板联网上报数据到服务器。 + +## 简述 +传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。 + +TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。TCP假设它可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从硬线连接到分组交换或电路交换网络的各种通信系统之上操作。 + +## TCP编程步骤 +  1、创建一个socket,用函数socket(); +  2、设置socket属性,用函数setsockopt();* 可选 +  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 +  4、设置要连接的对方的IP地址和端口等属性; +  5、连接服务器,用函数connect(); +  6、收发数据,用函数send()和recv(),或者read()和write(); +  7、关闭网络连接; + +## 特点 +(1)基于流的方式; +(2)面向连接; +(3)可靠通信方式; +(4)在网络状况不佳的时候尽量降低系统由于重传带来的带宽开销; +(5)通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点 + +## 结构体详解 + +``` +struct sockaddr_in { + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + uint8_t sin_zero[8]; +}; +``` + +**描述:** + +地址和端口信息 + +**参数:** + +| 名字 | 描述 | +| ------ | -------- | +|sin_family| 指代协议族,在socket编程中只能是AF_INET| +|sin_port| 存储端口号(使用网络字节顺序)| +|sin_addr| 存储IP地址,使用in_addr这个数据结构| +|sin_zero| 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节| + +**示例代码如下:** + +``` + struct sockaddr_in server_addr; + bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址 + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(_PROT_); + //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr); +``` + +------------------------------------ + +## 函数详解 + +``` +int WifiConnect(const char *ssid, const char *psk) +``` + +**描述:** 初始化网络,并连接指定wifi + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| ssid | 指定wifi的账户名 | +| psk | 指定wifi的密码 | + +``` +int socket(int domain, int type, int protocol) +``` + +**描述:** + +创建套接字 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| domain | 地址族,也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6 | +|type | 数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)和SOCK_RAW(原始套接字)| +|protocol | 传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议| + +``` +int connect(int fd, const struct sockaddr *addr, socklen_t len) +``` + +**描述:** + +连接服务器 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| fd | 服务器的socket | +|addr | 存放服务端用于通信的地址和端口| +|len | addr 结构体的大小| + + +``` +ssize_t recv(int fd, void *buf, size_t len, int flags) +``` + +**描述:** + +接收数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|fd | 客户端套接字| +|buf | 指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据| +|len | buf参数指向的缓冲区的长度(以字节为单位)| +|flags | 指定消息接收的类型。此参数的值通过逻辑或零或多个以下值形成:MSG_PEEK查看传入消息。数据被视为未读数据,下一个recv()或类似函数仍将返回此数据。| + + +``` +ssize_t send(int fd, const void *buf, size_t len, int flags) +``` + +**描述:** + +发送数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +| fd | 客户端套接字| +| buf | 存放应用程序要发送数据的缓冲区| +| len | 实际要发送的数据的字节数| +| flags | 表示消息传输的标志,一般置0| + +## 软件设计 + +**主要代码分析** + +```c +static void TCPClientTask(void) +{ + //连接Wifi + WifiConnect(WIFI_SSID,WIFI_PASSWORD); + + //创建socket + int sock_fd; + if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + printf("socket is error\r\n"); + exit(1); + } + printf("socket sock_fd=%d \r\n",sock_fd); + + + struct sockaddr_in server_addr; + bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址 + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(_PROT_); + //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr); + + printf("connect\n"); + char str[IP_LEN]; + inet_ntop(AF_INET, &server_addr.sin_addr, str, sizeof(str)); + printf("tcp server IP_addr: %s at PORT %d\n",str,ntohs(server_addr.sin_port)); + + int err_log = connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 主动连接服务器 + if(err_log != 0) + { + //error_handling("connect() error!"); + perror("connet"); + printf("\nconnect is error\n"); + close(sock_fd); + exit(-1); + } + + printf("err_log ========= %d\n", err_log); + unsigned char sendbufs[3][10]={{"0000"},{"1111"},{"2222"}}; + + for(int i=0; i<3; i++) + { + unsigned char* send_buf=sendbufs[i]; + if(send(sock_fd, send_buf, strlen(send_buf), 0)==-1) + { + printf("send error\n"); + close(sock_fd); + exit(-1); + } + + // + int size = recv(sock_fd, recvbuf,sizeof(recvbuf)-1,0); + if(size>0) + { + printf("recv msg: %s\n",recvbuf); + } + usleep(1000*1000); + } + + char send_buf[100]="exit"; + printf("send exit\n"); + if(send(sock_fd, send_buf, strlen(send_buf), 0)==-1) + { + perror("send error\n"); + close(sock_fd); + exit(-1); + } + usleep(1000*1000); + close(sock_fd); +} +``` + +## 编译调试 + +### 修改对接热点的账号密码 + +修改`tcp_client.c`第34行和35行的WiFi热点SSID和密码,改成自己环境中的WiFi热点。 + +``` +// 默认WiFi名和密码 +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" +``` + +### 修改服务端的ip地址和端口号 + +修改`tcp_client.c`第28行和30行的端口号和ip地址,改成自己创建的服务端ip地址。 + +``` +// 默认服务端的ip地址和端口号 +#define _PROT_ 8800 +#define _SERVER_IP_ "192.168.1.112" +``` + +### 修改 BUILD.gn 文件 + +修改 `applications/app/BUILD.gn` 路径中的 BUILD.gn 文件,指定 `network_tcpclient_demo` 参与编译。 + +``` + # "TW303_Network_mqttclient:network_mqttclient_example", + # "TW402_APP_oled_u8g2:app_oled_u8g2_example", + # "TW304_Network_tcpserver:network_tcpserver_demo", + "TW304_Network_tcpclient:network_tcpclient_demo", + # "TW305_Network_udpclient:network_udpclient_demo", +``` + +### 运行结果 + +示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,会打印连接到的Wifi热点信息,以及服务端地址信息,和服务端通信的信息 + +链接wifi成功,并打印IP信息 +``` +callback function for wifi connect + +WaitConnectResult:wait success[1]s +WiFi connect succeed! +begain to dhcp<-- DHCP state:Inprogress --> + +<-- DHCP state:Inprogress --> + +<-- DHCP state:Inprogress --> + +<-- DHCP state:OK --> +server : + server_id : 192.168.1.1 + mask : 255.255.255.0, 1 + gw : 192.168.1.1 + T0 : 7200 + T1 : 3600 + T2 : 6300 +clients <1> : + mac_idx mac addr state lease tries rto + 0 b4c9b9af6afe 192.168.1.114 10 0 1 3 +``` + +链接tcp服务端并进行信息通信 +``` +tcp server IP_addr: 192.168.1.112 at PORT 8800 + +send msg: 0000 + +recv msg: Hello! I'm Talkweb TCP Server! + +send msg: 1111 + +recv msg: Hello! I'm Talkweb TCP Server! + +send msg: 2222 + +recv msg: Hello! I'm Talkweb TCP Server! + +send exit +``` + + diff --git a/applications/app/TW304_Network_tcpclient/include/wifi_connect.h b/applications/app/TW304_Network_tcpclient/include/wifi_connect.h new file mode 100644 index 0000000000000000000000000000000000000000..c6bb2b6d2fd27b68327ad7105404f0ae89fafed6 --- /dev/null +++ b/applications/app/TW304_Network_tcpclient/include/wifi_connect.h @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __WIFI_CONNECT_H__ +#define __WIFI_CONNECT_H__ + +int WifiConnect(const char *ssid,const char *psk); + +#endif /* __WIFI_CONNECT_H__ */ + + + diff --git a/applications/app/TW304_Network_tcpclient/src/tcp_client.c b/applications/app/TW304_Network_tcpclient/src/tcp_client.c new file mode 100644 index 0000000000000000000000000000000000000000..617f2e6d9fb713a42035785e11a1704e1b729c1e --- /dev/null +++ b/applications/app/TW304_Network_tcpclient/src/tcp_client.c @@ -0,0 +1,141 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "lwip/sockets.h" +#include "wifi_connect.h" + +#include +#include + +/*TCP SERVER 参数*/ +#define _PROT_ 8800 +#define _SERVER_IP_ "192.168.1.112" +#define TCP_BACKLOG 5 +#define IP_LEN 16 + +/*wifi参数配置*/ +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" + +/*接收缓存和数据*/ +char recvbuf[1460]; + +int test() +{ + void* p; + if(sizeof(p)==4){ + printf( "This is a 32-bit machine.\n"); + } + else + printf( "This is a 64-bit machine.\n" ); + return 0; +} + +/*tcp服务器任务*/ +static void TCPClientTask(void) +{ + //连接Wifi + WifiConnect(WIFI_SSID,WIFI_PASSWORD); + + //创建socket + int sock_fd; + if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + printf("socket is error\r\n"); + exit(1); + } + //printf("socket sock_fd=%d \r\n",sock_fd); + + + struct sockaddr_in server_addr; + bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址 + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(_PROT_); + //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr); + + //printf("connect\n"); + char str[IP_LEN]; + inet_ntop(AF_INET, &server_addr.sin_addr, str, sizeof(str)); + printf("tcp server IP_addr: %s at PORT %d\n",str,ntohs(server_addr.sin_port)); + + int err_log = connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 主动连接服务器 + if(err_log != 0) + { + //error_handling("connect() error!"); + perror("connet"); + printf("\nconnect is error\n"); + close(sock_fd); + exit(-1); + } + + //printf("err_log ========= %d\n", err_log); + unsigned char sendbufs[3][10]={{"0000"},{"1111"},{"2222"}}; + + for(int i=0; i<3; i++) + { + unsigned char* send_buf=sendbufs[i]; + printf("send msg: %s\n",send_buf); + if(send(sock_fd, send_buf, strlen(send_buf), 0)==-1) + { + printf("send error\n"); + close(sock_fd); + exit(-1); + } + + // + int size = recv(sock_fd, recvbuf,sizeof(recvbuf)-1,0); + if(size>0) + { + printf("recv msg: %s\n",recvbuf); + } + usleep(1000*1000); + } + + char send_buf[100]="exit"; + printf("send exit\n"); + if(send(sock_fd, send_buf, strlen(send_buf), 0)==-1) + { + perror("send error\n"); + close(sock_fd); + exit(-1); + } + usleep(1000*1000); + close(sock_fd); +} + +static void TCPClientDemo(void) +{ + osThreadAttr_t attr; + + attr.name = "TCPClientTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = 1024*4; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)TCPClientTask, NULL, &attr) == NULL) + { + printf("[TCPServerDemo] Falied to create TCPClientTask!\n"); + } +} + +APP_FEATURE_INIT(TCPClientDemo); \ No newline at end of file diff --git a/applications/app/TW304_Network_tcpclient/src/wifi_connect.c b/applications/app/TW304_Network_tcpclient/src/wifi_connect.c new file mode 100644 index 0000000000000000000000000000000000000000..1acd81a4bf5f9fb5a85f07d5635134e1f938a938 --- /dev/null +++ b/applications/app/TW304_Network_tcpclient/src/wifi_connect.c @@ -0,0 +1,277 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" + +#include "lwip/netif.h" +#include "lwip/netifapi.h" +#include "lwip/ip4_addr.h" +#include "lwip/api_shell.h" + +#include "wifi_device.h" + +#define DEF_TIMEOUT 15 +#define ONE_SECOND 1 + +#define SELECT_WLAN_PORT "wlan0" +#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_PSK + +static int g_staScanSuccess = 0; +static int g_ConnectSuccess = 0; +static int ssid_count = 0; + +WifiErrorCode g_error; +WifiEvent g_wifiEventHandler = {0}; + +static void WiFiInit(void); +static void WaitSacnResult(void); +static int WaitConnectResult(void); +static void OnWifiScanStateChangedHandler(int state, int size); +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info); +static void OnHotspotStaJoinHandler(StationInfo *info); +static void OnHotspotStateChangedHandler(int state); +static void OnHotspotStaLeaveHandler(StationInfo *info); + +static void OnHotspotStaJoinHandler(StationInfo *info) +{ + (void)info; + printf("STA join AP\n"); + return; +} + +static void OnHotspotStaLeaveHandler(StationInfo *info) +{ + (void)info; + printf("HotspotStaLeave:info is null.\n"); + return; +} + +static void OnHotspotStateChangedHandler(int state) +{ + printf("HotspotStateChanged:state is %d.\n", state); + return; +} + +static void WiFiInit(void) +{ + printf("<--Wifi Init-->\r\n"); + g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler; + g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler; + g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler; + g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler; + g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler; + g_error = RegisterWifiEvent(&g_wifiEventHandler); + if (g_error != WIFI_SUCCESS) + { + printf("register wifi event fail!\r\n"); + } + else + { + printf("register wifi event succeed!\r\n"); + } +} + +static void OnWifiScanStateChangedHandler(int state, int size) +{ + (void)state; + if (size > 0) + { + ssid_count = size; + g_staScanSuccess = 1; + } + return; +} + +int WifiConnect(const char *ssid, const char *psk) +{ + WifiScanInfo *wifi_info = NULL; + unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT; + static struct netif *g_lwip_netif = NULL; + WifiDeviceConfig select_ap_config = {0}; + + osDelay(200); + printf("<--System Init-->\r\n"); + + // 初始化WIFI + WiFiInit(); + + if (EnableWifi() != WIFI_SUCCESS) + { + printf("EnableWifi failed, error = %d\n", g_error); + return -1; + } + + //判断WIFI是否激活 + if (IsWifiActive() == 0) + { + printf("Wifi station is not actived.\n"); + return -1; + } + + //分配空间,保存WiFi信息 + wifi_info = malloc(sizeof(WifiScanInfo) * WIFI_SCAN_HOTSPOT_LIMIT); + if (wifi_info == NULL) + { + printf("faild to create wifiscanInfo.\n"); + return -1; + } + + do + { + //重置标志位 + ssid_count = 0; + g_staScanSuccess = 0; + //开启wifi扫描 + Scan(); + + //等待扫描结果 + WaitSacnResult(); + + //获取扫描列表 + g_error = GetScanInfoList(wifi_info, &size); + } while (g_staScanSuccess != 1); + + //打印WiFi列表 + printf("********************\r\n"); + for (uint8_t i = 0; i < ssid_count; i++) + { + printf("no:%03d, ssid:%-30s, rssi:%5d\r\n", i + 1, wifi_info[i].ssid, wifi_info[i].rssi / 100); + } + printf("********************\r\n"); + + //插件指定的wifi是否存在 + for (uint8_t i = 0; i < ssid_count; i++) + { + if (strcmp(ssid, wifi_info[i].ssid) == 0) + { + int result; + printf("Select:%3d wireless, Waiting...\r\n", i + 1); + //拷贝要连接的热点信息 + strcpy(select_ap_config.ssid, wifi_info[i].ssid); + strcpy(select_ap_config.preSharedKey, psk); + select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE; + + if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS) + { + if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1) + { + printf("WiFi connect succeed!\r\n"); + g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT); + break; + } + } + } + + if (i == ssid_count - 1) + { + printf("ERROR: No wifi as expected\r\n"); + while (1) + osDelay(100); + } + } + + //启动DHCP + if (g_lwip_netif) + { + dhcp_start(g_lwip_netif); + printf("begain to dhcp"); + } + + //等待DHCP + for (;;) + { + if (dhcp_is_bound(g_lwip_netif) == ERR_OK) + { + printf("<-- DHCP state:OK -->\r\n"); + + //打印获取到的IP信息 + netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL); + break; + } + + printf("<-- DHCP state:Inprogress -->\r\n"); + osDelay(100); + } + + osDelay(100); + if (wifi_info != NULL) + { + free(wifi_info); + wifi_info = NULL; + } + return 0; +} + +static int WaitConnectResult(void) +{ + int ConnectTimeout = DEF_TIMEOUT; + while (ConnectTimeout > 0) + { + sleep(1); + ConnectTimeout--; + if (g_ConnectSuccess == 1) + { + printf("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - ConnectTimeout)); + break; + } + } + if (ConnectTimeout <= 0) + { + printf("WaitConnectResult:timeout!\n"); + return 0; + } + + return 1; +} + +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info) +{ + (void)info; + + if (state > 0) + { + g_ConnectSuccess = 1; + printf("callback function for wifi connect\r\n"); + } + else + { + printf("connect error,please check ssid and password\r\n"); + } + return; +} + +static void WaitSacnResult(void) +{ + int scanTimeout = DEF_TIMEOUT; + while (scanTimeout > 0) + { + sleep(ONE_SECOND); + scanTimeout--; + if (g_staScanSuccess == 1) + { + printf("WaitSacnResult: wait success [%d] s \n", (DEF_TIMEOUT - scanTimeout)); + break; + } + } + if (scanTimeout <= 0) + { + printf("WaitSacnResult:timeout!\n"); + } +} diff --git a/applications/app/TW304_Network_tcpserver/BUILD.gn b/applications/app/TW304_Network_tcpserver/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..766900bb90bf843f7fddeb75d72a30c6b79959ac --- /dev/null +++ b/applications/app/TW304_Network_tcpserver/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Talkweb Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +static_library("network_tcpserver_demo") { + sources = [ + "src/wifi_connect.c", + "src/tcp_server.c", + ] + + cflags = [ "-Wno-unused-variable" ] + cflags += [ "-Wno-unused-but-set-variable" ] + + include_dirs = [ + "//foundation/communication/wifi_lite/interfaces/wifiservice" , + "include" + ] +} \ No newline at end of file diff --git a/applications/app/TW304_Network_tcpserver/README.md b/applications/app/TW304_Network_tcpserver/README.md new file mode 100644 index 0000000000000000000000000000000000000000..31772253933d4a9bb1d0a5c2ecdde243d3e95519 --- /dev/null +++ b/applications/app/TW304_Network_tcpserver/README.md @@ -0,0 +1,451 @@ +# Niobe开发板TCP联网演示 + +本案例程序将演示怎么在拓维Niobe WiFi IoT Core开发板上编写一个创建tcp服务器的业务程序,实现开发板联网与tcp客户端数据通信。 + +## 简述 +传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。 + +TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。TCP假设它可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从硬线连接到分组交换或电路交换网络的各种通信系统之上操作。 + +## TCP编程步骤 +  1、创建一个socket,用函数socket(); +  2、设置socket属性,用函数setsockopt(); * 可选 +  3、绑定IP地址、端口等信息到socket上,用函数bind(); +  4、开启监听,用函数listen(); +  5、接收客户端上来的连接,用函数accept(); +  6、收发数据,用函数send()和recv(),或者read()和write(); +  7、关闭网络连接; +  8、关闭监听 + +## 特点 +(1)基于流的方式; +(2)面向连接; +(3)可靠通信方式; +(4)在网络状况不佳的时候尽量降低系统由于重传带来的带宽开销; +(5)通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点 + +## 结构体详解 + +``` +struct sockaddr_in { + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + uint8_t sin_zero[8]; +}; +``` + +**描述:** + +地址和端口信息 + +**参数:** + +| 名字 | 描述 | +| ------ | -------- | +|sin_family| 指代协议族,在socket编程中只能是AF_INET| +|sin_port| 存储端口号(使用网络字节顺序)| +|sin_addr| 存储IP地址,使用in_addr这个数据结构| +|sin_zero| 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节| + +**示例代码如下:** + +``` + //服务端地址信息 + struct sockaddr_in server_sock; + bzero(&server_sock, sizeof(server_sock)); + server_sock.sin_family = AF_INET; + //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, "192.168.1.121", &server_sock.sin_addr); + server_sock.sin_port = htons(_PROT_); +``` + +------------------------------------ + +``` +typedef struct { + unsigned long fds_bits[FD_SETSIZE / 8 / sizeof(long)]; +} fd_set; +``` + +**描述:** + +文件描述符集合 + +**操作函数:** + +| 名字 | 描述 | +| ------ | -------- | +|void FD_CLR(int fd, fd_set *set)| 清除某一个被监视的文件描述符| +|int FD_ISSET(int fd, fd_set *set)|测试一个文件描述符是否是集合中的一员| +|void FD_SET(int fd, fd_set *set)| 添加一个文件描述符,将set中的某一位设置成1| +|void FD_ZERO(fd_set *set)| 清空集合中的文件描述符,将每一位都设置为0| + +**示例代码如下:** + +``` + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock_fd,&fds);//将sock_fd添加至fds + + if(FD_ISSET(sock_fd,&fds))//判断sock_fd是否在fds中 + ;// + +``` + +## 函数详解 + +``` +int WifiConnect(const char *ssid, const char *psk) +``` + +**描述:** +初始化网络,并连接指定wifi + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| ssid | 指定wifi的账户名 | +| psk | 指定wifi的密码 | + +``` +int socket(int domain, int type, int protocol) +``` + +**描述:** + +创建套接字 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| domain | 地址族,也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6 | +|type | 数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)和SOCK_RAW(原始套接字)| +|protocol | 传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议| + +``` +int bind(int fd, const struct sockaddr *addr, socklen_t len) +``` + +**描述:** + +把用于通信的地址和端口绑定到 socket 上 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| fd | 需要绑定的socket | +|addr | 存放服务端用于通信的地址和端口| +|len | addr 结构体的大小| + + +``` +int listen(int fd, int backlog) +``` + +**描述:** + +将套接字置于侦听传入连接的状态 + +**参数:** + +| 名字 | 描述 | +| ------ | -------- | +| fd | 服务端的socket,也就是socket函数创建的 | +| backlog | 定义“fd”的挂起连接队列可能增长到的最大长度 | + +``` +int accept(int socket, struct sockaddr *address, socklen_t *address_len) +``` + +**描述:** + +接收客户端上来的连接 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +| socket | 服务端的套接字| +| address | 存放服务端用于通信的地址和端口| +| address_len | 指向address结构体大小的指针| + +``` +ssize_t recv(int fd, void *buf, size_t len, int flags) +``` + +**描述:** + +接收远端主机经指定的socket 传来的数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|fd | 服务端的套接字| +|buf | 指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据| +|len | buf参数指向的缓冲区的长度(以字节为单位)| +|flags | 指定消息接收的类型。此参数的值通过逻辑或零或多个以下值形成:MSG_PEEK查看传入消息。数据被视为未读数据,下一个recv()或类似函数仍将返回此数据。| + + +``` +ssize_t send(int fd, const void *buf, size_t len, int flags) +``` + +**描述:** + +发送数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +| fd | 客户端的套接字| +| buf | 存放应用程序要发送数据的缓冲区| +| len | 实际要发送的数据的字节数| +| flags | 表示消息传输的标志,一般置0| + +``` +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +``` + +**描述:** + +IO多路复用 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|nfds | 表示集合中所有文件描述符的范围| +|readfds | select监视的可读文件句柄集合| +|writefds | select监视的可写文件句柄集合| +|exceptfds | select监视的异常文件句柄集合| +|timeout | 本次select()的超时结束时间,NULL表示永久等待| + +## 软件设计 + +**主要代码分析** + +```c +static void TCPServerTaskWithSelect(void) +{ + //服务端地址信息 + struct sockaddr_in server_sock; + //客户端地址信息 + struct sockaddr_in client_sock; + int sin_size = sizeof(struct sockaddr_in); + struct sockaddr_in *cli_addr; + //连接Wifi + if(WifiConnect(WIFI_SSID,WIFI_PASSWORD)!=0) + { + perror("Wifi connect error!"); + exit(1); + } + in_addr_t localIpaddr = GetLocalIpaddr(); + printf("ip = %x\n",localIpaddr); + + //创建socket + printf("socket begin\n"); + int sock_fd=-1,new_fd=-1; + if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror("socket is error\r\n"); + exit(1); + } + printf("sock_fd=%d\n",sock_fd); + + bzero(&server_sock, sizeof(server_sock)); + server_sock.sin_family = AF_INET; + //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, "192.168.1.114", &server_sock.sin_addr); + server_sock.sin_port = htons(_PROT_); + char str[IP_LEN]; + inet_ntop(AF_INET, &server_sock.sin_addr, str, sizeof(str)); + printf("tcp server IP_addr: %s at PORT %d\n",str,ntohs(server_sock.sin_port)); + + //调用bind函数绑定socket和地址 + if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1) + { + perror("bind is error\r\n"); + exit(1); + } + //调用listen函数监听(指定port监听) + if (listen(sock_fd, TCP_BACKLOG) == -1) + { + perror("listen is error\r\n"); + exit(1); + } + //init fd_arr + printf("start select\r\n"); + int i=0; + for(;i<_MAX_SIZE_;++i) + { + fd_arr[i]=-1; + } + add_fd_arr(sock_fd); + fd_set fds; + FD_ZERO(&fds); + + while(true) + { + //更新max_fd和文件描述符集 + max_fd=sock_fd; + for(int ii=0; ii<_MAX_SIZE_; ii++) + { + if(fd_arr[ii]!=-1) + { + FD_SET(fd_arr[ii],&fds); + if(fd_arr[ii]>max_fd) + { + max_fd=fd_arr[ii]; + } + } + } + //printf("update max_fd = %d\n",max_fd); + printf("select wait...\n"); + int ret = select(max_fd + 1, &fds, NULL, NULL, NULL); + if(ret<=0){ + //超时等 + printf("[DISCOVERY]ret:%d\n", ret); + continue; + } + if(FD_ISSET(sock_fd,&fds)) //判断是否发生accept + { + new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size); + //printf("accept new fd = %d\n",new_fd); + if(-1!=new_fd) + { + //打印客户端的ip和port + char str[IP_LEN]; + inet_ntop(AF_INET, &client_sock.sin_addr, str, sizeof(str)); + printf("connected from %s at PORT %d\n",str,ntohs(client_sock.sin_port)); + if(1==add_fd_arr(new_fd)) + { + perror("fd_arr is full,close new_fd\n"); + close(new_fd);//此处比较粗陋,应该关闭久未发生信息交付的fd + } + } + continue; + } + //客户端发来信息 + for(i=0;i<_MAX_SIZE_;++i) + { + if(fd_arr[i]!=-1&&FD_ISSET(fd_arr[i],&fds)) + { + char buf[1024]; + memset(buf,'\0',sizeof(buf)); + ssize_t size=recv(fd_arr[i],buf,sizeof(buf)-1,0); + //"exit"特殊字符串,表示客户端申请断开链接 + if(size==0 || size==-1 || memcmp(buf,"exit",size)==0) + { + printf("remote client fd is %d, close,size is %d,msg is %s\n",fd_arr[i],size,buf); + FD_CLR(fd_arr[i],&fds); + close(fd_arr[i]); + fd_arr[i]=-1; + }else + { + printf("recv: fd:%d,msg:%s",fd_arr[i],buf); + if ((ret = send(fd_arr[i], tcpSendBuf, strlen(tcpSendBuf) + 1, 0)) == -1) + { + perror("send error \r\n"); + } + } + osDelay(10); + continue; + } + } + } + close(sock_fd); + FD_CLR(sock_fd,&fds); + sock_fd=-1; + return ; +} +``` + +## 编译调试 + +### 修改对接热点的账号密码 + +修改`tcp_server.c`第34行和35行的WiFi热点SSID和密码,改成自己环境中的WiFi热点。 + +``` +// 默认WiFi名和密码 +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" +``` + +### 修改服务端的ip地址和端口号 + +修改`tcp_server.c`第28行和30行的端口号和ip地址,改成自己链接上wifi后的ip地址,此处应该从wifi的sdk中获取本身的ip地址,但还未找到相应api。 + +``` +// 默认服务端的ip地址和端口号 +#define _PROT_ 8800 +#define _SERVER_IP_ "192.168.1.112" +``` + +### 修改 BUILD.gn 文件 + +修改 `applications/app/BUILD.gn` 路径中的 BUILD.gn 文件,指定 `network_tcpserver_demo` 参与编译。 + +``` + # "TW303_Network_mqttclient:network_mqttclient_example", + # "TW402_APP_oled_u8g2:app_oled_u8g2_example", + "TW304_Network_tcpserver:network_tcpserver_demo", + # "TW304_Network_tcpclient:network_tcpclient_demo", + # "TW305_Network_udpclient:network_udpclient_demo", +``` + +### 运行结果 + +示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,会打印连接到的Wifi热点信息,以及开启tcp服务端,等待客户端连接 + +链接wifi成功,并打印IP信息 +``` +callback function for wifi connect + +WaitConnectResult:wait success[1]s +WiFi connect succeed! +begain to dhcp<-- DHCP state:Inprogress --> + +<-- DHCP state:OK --> +server : + server_id : 192.168.1.1 + mask : 255.255.255.0, 1 + gw : 192.168.1.1 + T0 : 7200 + T1 : 3600 + T2 : 6300 +clients <1> : + mac_idx mac addr state lease tries rto + 0 849dc22100d4 192.168.1.112 10 0 1 4 +``` + +开启tcp服务端,并进行监听等待 +``` +tcp server IP_addr: 192.168.1.112 at PORT 8800 +select wait... +``` + +有客户端接入并进行通信 +``` +connected from 192.168.1.114 at PORT 61941 +select wait... +recv: fd:1,msg:0000 +select wait... + +recv: fd:1,msg:1111 +select wait... + +recv: fd:1,msg:2222 +select wait... + +remote client fd is 1, close,size is 4,msg is exit + +select wait... + +``` diff --git a/applications/app/TW304_Network_tcpserver/include/wifi_connect.h b/applications/app/TW304_Network_tcpserver/include/wifi_connect.h new file mode 100644 index 0000000000000000000000000000000000000000..c6bb2b6d2fd27b68327ad7105404f0ae89fafed6 --- /dev/null +++ b/applications/app/TW304_Network_tcpserver/include/wifi_connect.h @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __WIFI_CONNECT_H__ +#define __WIFI_CONNECT_H__ + +int WifiConnect(const char *ssid,const char *psk); + +#endif /* __WIFI_CONNECT_H__ */ + + + diff --git a/applications/app/TW304_Network_tcpserver/src/tcp_server.c b/applications/app/TW304_Network_tcpserver/src/tcp_server.c new file mode 100644 index 0000000000000000000000000000000000000000..078a6c94ccd7d255fb7af7431228c8569e7ea2f0 --- /dev/null +++ b/applications/app/TW304_Network_tcpserver/src/tcp_server.c @@ -0,0 +1,299 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "lwip/sockets.h" +#include "wifi_connect.h" + +#include +#include +#include + +/*TCP SERVER 参数*/ +#define _PROT_ 8800 +#define TCP_BACKLOG 5 +#define _SERVER_IP_ "192.168.1.112" +#define IP_LEN 16 + +/*wifi参数配置*/ +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" + +/*接收缓存和数据*/ +char recvbuf[1460]; +char *tcpSendBuf = "Hello! I'm Talkweb TCP Server!"; + + +#define _MAX_SIZE_ 10 +int fd_arr[_MAX_SIZE_]; +int max_fd=0; +static void Useage(const char* proc) +{ + printf("Useage:%s,[ip][port]"); + exit(1); +} +static int add_fd_arr(int fd) +{ + //fd add to fd_arr + int i=0; + for(;i<_MAX_SIZE_;++i) + { + if(fd_arr[i]==-1) + { + fd_arr[i]=fd; + return 0; + } + } + return 1; +} + + +/*tcp服务器任务,采用select*/ +static void TCPServerTaskWithSelect(void) +{ + //服务端地址信息 + struct sockaddr_in server_sock; + //客户端地址信息 + struct sockaddr_in client_sock; + int sin_size = sizeof(struct sockaddr_in); + struct sockaddr_in *cli_addr; + //连接Wifi + if(WifiConnect(WIFI_SSID,WIFI_PASSWORD)!=0) + { + perror("Wifi connect error!"); + exit(1); + } + //in_addr_t localIpaddr = GetLocalIpaddr(); + //printf("ip = %x\n",localIpaddr); + + //创建socket + //printf("socket begin\n"); + int sock_fd=-1,new_fd=-1; + if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror("socket is error\r\n"); + exit(1); + } + //printf("sock_fd=%d\n",sock_fd); + + bzero(&server_sock, sizeof(server_sock)); + server_sock.sin_family = AF_INET; + //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_sock.sin_addr); + server_sock.sin_port = htons(_PROT_); + char str[IP_LEN]; + inet_ntop(AF_INET, &server_sock.sin_addr, str, sizeof(str)); + printf("tcp server IP_addr: %s at PORT %d\n",str,ntohs(server_sock.sin_port)); + + //调用bind函数绑定socket和地址 + if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1) + { + perror("bind is error\r\n"); + exit(1); + } + //调用listen函数监听(指定port监听) + if (listen(sock_fd, TCP_BACKLOG) == -1) + { + perror("listen is error\r\n"); + exit(1); + } + //init fd_arr + //printf("start select\r\n"); + int i=0; + for(;i<_MAX_SIZE_;++i) + { + fd_arr[i]=-1; + } + add_fd_arr(sock_fd); + fd_set fds; + FD_ZERO(&fds); + + while(true) + { + //更新max_fd和文件描述符集 + max_fd=sock_fd; + for(int ii=0; ii<_MAX_SIZE_; ii++) + { + if(fd_arr[ii]!=-1) + { + FD_SET(fd_arr[ii],&fds); + if(fd_arr[ii]>max_fd) + { + max_fd=fd_arr[ii]; + } + } + } + //printf("update max_fd = %d\n",max_fd); + printf("select wait...\n"); + int ret = select(max_fd + 1, &fds, NULL, NULL, NULL); + if(ret<=0){ + //超时等 + printf("[DISCOVERY]ret:%d\n", ret); + continue; + } + if(FD_ISSET(sock_fd,&fds)) //判断是否发生accept + { + new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size); + //printf("accept new fd = %d\n",new_fd); + if(-1!=new_fd) + { + //打印客户端的ip和port + char str[IP_LEN]; + inet_ntop(AF_INET, &client_sock.sin_addr, str, sizeof(str)); + printf("connected from %s at PORT %d\n",str,ntohs(client_sock.sin_port)); + if(1==add_fd_arr(new_fd)) + { + perror("fd_arr is full,close new_fd\n"); + close(new_fd);//此处比较粗陋,应该关闭久未发生信息交付的fd + } + } + continue; + } + //客户端发来信息 + for(i=0;i<_MAX_SIZE_;++i) + { + if(fd_arr[i]!=-1&&FD_ISSET(fd_arr[i],&fds)) + { + char buf[1024]; + memset(buf,'\0',sizeof(buf)); + ssize_t size=recv(fd_arr[i],buf,sizeof(buf)-1,0); + //"exit"特殊字符串,表示客户端申请断开链接 + if(size==0 || size==-1 || memcmp(buf,"exit",size)==0) + { + printf("remote client fd is %d, close,size is %d,msg is %s\n",fd_arr[i],size,buf); + FD_CLR(fd_arr[i],&fds); + close(fd_arr[i]); + fd_arr[i]=-1; + }else + { + printf("recv: fd:%d,msg:%s",fd_arr[i],buf); + if ((ret = send(fd_arr[i], tcpSendBuf, strlen(tcpSendBuf) + 1, 0)) == -1) + { + perror("send error \r\n"); + } + } + osDelay(10); + continue; + } + } + } + close(sock_fd); + FD_CLR(sock_fd,&fds); + sock_fd=-1; + return ; +} + +/*tcp服务器任务*/ +static void TCPServerTask(void) +{ + //服务端地址信息 + struct sockaddr_in server_sock; + //客户端地址信息 + struct sockaddr_in client_sock; + int sin_size; + struct sockaddr_in *cli_addr; + //连接Wifi + WifiConnect(WIFI_SSID,WIFI_PASSWORD); + + //创建socket + int sock_fd=-1,new_fd=-1; + if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror("socket is error\r\n"); + exit(1); + } + printf("sock_fd=%d\n",sock_fd); + + bzero(&server_sock, sizeof(server_sock)); + server_sock.sin_family = AF_INET; + //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, "192.168.1.121", &server_sock.sin_addr); + server_sock.sin_port = htons(_PROT_); + + //调用bind函数绑定socket和地址 + if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1) + { + perror("bind is error\r\n"); + exit(1); + } + //调用listen函数监听(指定port监听) + if (listen(sock_fd, TCP_BACKLOG) == -1) + { + perror("listen is error\r\n"); + exit(1); + } + printf("start accept\r\n"); + + //打印客户端的ip和port + char str[IP_LEN]; + printf("server ip %s at PORT %d\n",inet_ntop(AF_INET, &server_sock.sin_addr, str, sizeof(str)),ntohs(server_sock.sin_port)); + + + //调用accept函数从队列中 + while (1) + { + sin_size = sizeof(struct sockaddr_in); + + if ((new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size)) == -1) + { + perror("accept"); + continue; + } + //打印客户端的ip和port + char str[IP_LEN]; + printf("connected from %s at PORT %d\n",inet_ntop(AF_INET, &client_sock.sin_addr, str, sizeof(str)),ntohs(client_sock.sin_port)); + //处理目标 + int ret; + + while (1) + { + memset(recvbuf,0x0,sizeof(recvbuf)); + if ((ret = recv(new_fd, recvbuf, sizeof(recvbuf), 0)) == -1) + { + printf("recv error \r\n"); + } + printf("recv :%s\r\n", recvbuf); + if ((ret = send(new_fd, tcpSendBuf, strlen(tcpSendBuf) + 1, 0)) == -1) + { + perror("send : "); + } + osDelay(100); + } + close(new_fd); + } +} + +static void TCPServerDemo(void) +{ + osThreadAttr_t attr; + + attr.name = "TCPServerTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = 1024*4; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)TCPServerTaskWithSelect, NULL, &attr) == NULL) + { + printf("[TCPServerDemo] Falied to create TCPServerTask!\n"); + } +} + +APP_FEATURE_INIT(TCPServerDemo); \ No newline at end of file diff --git a/applications/app/TW304_Network_tcpserver/src/wifi_connect.c b/applications/app/TW304_Network_tcpserver/src/wifi_connect.c new file mode 100644 index 0000000000000000000000000000000000000000..0830ef1c099534c52430c99e413a03df58fd3d59 --- /dev/null +++ b/applications/app/TW304_Network_tcpserver/src/wifi_connect.c @@ -0,0 +1,291 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" + +#include "lwip/netif.h" +#include "lwip/netifapi.h" +#include "lwip/ip4_addr.h" +#include "lwip/api_shell.h" + +#include "wifi_device.h" + +#define DEF_TIMEOUT 15 +#define ONE_SECOND 1 + +#define SELECT_WLAN_PORT "wlan0" +#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_PSK + +static int g_staScanSuccess = 0; +static int g_ConnectSuccess = 0; +static int ssid_count = 0; + +WifiErrorCode g_error; +WifiEvent g_wifiEventHandler = {0}; + +static struct netif *g_lwip_netif = NULL; + +static void WiFiInit(void); +static void WaitSacnResult(void); +static int WaitConnectResult(void); +static void OnWifiScanStateChangedHandler(int state, int size); +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info); +static void OnHotspotStaJoinHandler(StationInfo *info); +static void OnHotspotStateChangedHandler(int state); +static void OnHotspotStaLeaveHandler(StationInfo *info); + +static void OnHotspotStaJoinHandler(StationInfo *info) +{ + (void)info; + printf("STA join AP\n"); + return; +} + +static void OnHotspotStaLeaveHandler(StationInfo *info) +{ + (void)info; + printf("HotspotStaLeave:info is null.\n"); + return; +} + +static void OnHotspotStateChangedHandler(int state) +{ + printf("HotspotStateChanged:state is %d.\n", state); + return; +} + +static void WiFiInit(void) +{ + printf("<--Wifi Init-->\r\n"); + g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler; + g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler; + g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler; + g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler; + g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler; + g_error = RegisterWifiEvent(&g_wifiEventHandler); + if (g_error != WIFI_SUCCESS) + { + printf("register wifi event fail!\r\n"); + } + else + { + printf("register wifi event succeed!\r\n"); + } +} + +static void OnWifiScanStateChangedHandler(int state, int size) +{ + (void)state; + if (size > 0) + { + ssid_count = size; + g_staScanSuccess = 1; + } + return; +} + +//改函数需在函数WifiConnect调用成功之后才能获取到正确的ip地址 +in_addr_t GetLocalIpaddr(void) +{ + if(g_lwip_netif) + { + return g_lwip_netif->ip_addr.u_addr.ip4.addr; + } + else + { + return INADDR_ANY; + } +} + +int WifiConnect(const char *ssid, const char *psk) +{ + WifiScanInfo *wifi_info = NULL; + unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT; + WifiDeviceConfig select_ap_config = {0}; + + osDelay(200); + printf("<--System Init-->\r\n"); + + // 初始化WIFI + WiFiInit(); + + if (EnableWifi() != WIFI_SUCCESS) + { + printf("EnableWifi failed, error = %d\n", g_error); + return -1; + } + + //判断WIFI是否激活 + if (IsWifiActive() == 0) + { + printf("Wifi station is not actived.\n"); + return -1; + } + + //分配空间,保存WiFi信息 + wifi_info = malloc(sizeof(WifiScanInfo) * WIFI_SCAN_HOTSPOT_LIMIT); + if (wifi_info == NULL) + { + printf("faild to create wifiscanInfo.\n"); + return -1; + } + + do + { + //重置标志位 + ssid_count = 0; + g_staScanSuccess = 0; + //开启wifi扫描 + Scan(); + + //等待扫描结果 + WaitSacnResult(); + + //获取扫描列表 + g_error = GetScanInfoList(wifi_info, &size); + } while (g_staScanSuccess != 1); + + //打印WiFi列表 + printf("********************\r\n"); + for (uint8_t i = 0; i < ssid_count; i++) + { + printf("no:%03d, ssid:%-30s, rssi:%5d\r\n", i + 1, wifi_info[i].ssid, wifi_info[i].rssi / 100); + } + printf("********************\r\n"); + + //插件指定的wifi是否存在 + for (uint8_t i = 0; i < ssid_count; i++) + { + if (strcmp(ssid, wifi_info[i].ssid) == 0) + { + int result; + printf("Select:%3d wireless, Waiting...\r\n", i + 1); + //拷贝要连接的热点信息 + strcpy(select_ap_config.ssid, wifi_info[i].ssid); + strcpy(select_ap_config.preSharedKey, psk); + select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE; + + if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS) + { + if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1) + { + printf("WiFi connect succeed!\r\n"); + g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT); + break; + } + } + } + + if (i == ssid_count - 1) + { + printf("ERROR: No wifi as expected\r\n"); + while (1) + osDelay(100); + } + } + + //启动DHCP + if (g_lwip_netif) + { + dhcp_start(g_lwip_netif); + printf("begain to dhcp"); + } + + //等待DHCP + for (;;) + { + if (dhcp_is_bound(g_lwip_netif) == ERR_OK) + { + printf("<-- DHCP state:OK -->\r\n"); + + //打印获取到的IP信息 + netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL); + break; + } + + printf("<-- DHCP state:Inprogress -->\r\n"); + osDelay(100); + } + + osDelay(100); + if (wifi_info != NULL) + { + free(wifi_info); + wifi_info = NULL; + } + return 0; +} + +static int WaitConnectResult(void) +{ + int ConnectTimeout = DEF_TIMEOUT; + while (ConnectTimeout > 0) + { + sleep(1); + ConnectTimeout--; + if (g_ConnectSuccess == 1) + { + printf("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - ConnectTimeout)); + break; + } + } + if (ConnectTimeout <= 0) + { + printf("WaitConnectResult:timeout!\n"); + return 0; + } + + return 1; +} + +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info) +{ + (void)info; + + if (state > 0) + { + g_ConnectSuccess = 1; + printf("callback function for wifi connect\r\n"); + } + else + { + printf("connect error,please check ssid and password\r\n"); + } + return; +} + +static void WaitSacnResult(void) +{ + int scanTimeout = DEF_TIMEOUT; + while (scanTimeout > 0) + { + sleep(ONE_SECOND); + scanTimeout--; + if (g_staScanSuccess == 1) + { + printf("WaitSacnResult: wait success [%d] s \n", (DEF_TIMEOUT - scanTimeout)); + break; + } + } + if (scanTimeout <= 0) + { + printf("WaitSacnResult:timeout!\n"); + } +} diff --git a/applications/app/TW305_Network_udpclient/BUILD.gn b/applications/app/TW305_Network_udpclient/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..0f92f0b89fe8b758eb42b010086e9f8adf26831c --- /dev/null +++ b/applications/app/TW305_Network_udpclient/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Talkweb Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +static_library("network_udpclient_demo") { + sources = [ + "src/wifi_connect.c", + "src/udp_client.c", + ] + + cflags = [ "-Wno-unused-variable" ] + cflags += [ "-Wno-unused-but-set-variable" ] + + include_dirs = [ + "//foundation/communication/wifi_lite/interfaces/wifiservice" , + "include" + ] +} \ No newline at end of file diff --git a/applications/app/TW305_Network_udpclient/README.md b/applications/app/TW305_Network_udpclient/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f4e1f458d0835d3f40db0e2cbf24acf88667c6fa --- /dev/null +++ b/applications/app/TW305_Network_udpclient/README.md @@ -0,0 +1,297 @@ +# Niobe开发板UDP联网演示 + +本案例程序将演示怎么在拓维Niobe WiFi IoT Core开发板上编写一个连接udp服务器的业务程序,实现开发板联网上报数据到服务器。 + +## 简述 +UDP是用户数据包协议(UDP,User Datagram Protocol), 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。RFC 768 描述了 UDP。 + +Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情。面向连接的是 TCP,该协议几乎做了所有的事情 + +## UDP客户端编程步骤 +  1、创建一个socket,用函数socket(); +  2、设置socket属性,用函数setsockopt();* 可选 +  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 +  4、设置对方的IP地址和端口等属性; +  5、循环接收/发送数据,用函数recvfrom()/sendto(); +  6、关闭网络连接; + +## 特点 +1、UDP是无连接的,即发送数据之前不需要建立连接; +2、UDP使用尽最大努力交付,即不保证可靠交付; +3、UDP是面向报文的; +4、UDP支持一对一、一对多、多对一和多对多的交互通信等。 + +## 结构体详解 + +``` +struct sockaddr_in { + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + uint8_t sin_zero[8]; +}; +``` + +**描述:** + +地址和端口信息 + +**参数:** + +| 名字 | 描述 | +| ------ | -------- | +|sin_family| 指代协议族,在socket编程中只能是AF_INET| +|sin_port| 存储端口号(使用网络字节顺序)| +|sin_addr| 存储IP地址,使用in_addr这个数据结构| +|sin_zero| 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节| + +**示例代码如下:** + +``` + struct sockaddr_in server_addr; + bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址 + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(_PROT_); + //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr); + int sin_size = sizeof(struct sockaddr_in); +``` +-------------------------------------------- +``` +struct sockaddr { + sa_family_t sa_family; + char sa_data[14]; +}; +``` +**描述:** + +通用的套接字地址,与sockaddr_in类似 + +**参数** + +| 名字 | 描述 | +| ------ | -------- | +|sa_family| 地址族| +|sa_data|14字节,包含套接字中的目标地址和端口信息 | + +------------------------------------ + +## 函数详解 + +``` +int WifiConnect(const char *ssid, const char *psk) +``` + +**描述:** 初始化网络,并连接指定wifi + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| ssid | 指定wifi的账户名 | +| psk | 指定wifi的密码 | + +``` +int socket(int domain, int type, int protocol) +``` + +**描述:** + +创建套接字 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| domain | 地址族,也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6 | +|type | 数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)和SOCK_RAW(原始套接字)| +|protocol | 传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议| + + +``` +ssize_t recvfrom(int fd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict alen) +``` + +**描述:** + +接收数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|fd | 客户端套接字| +|buf | UDP数据报缓存区(包含所接收的数据)| +|len | 缓冲区长度| +|flags | 调用操作方式(一般设置为0)| +|addr | 指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换)| +|alen | 指针,指向addr结构体长度值| + + +``` +ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t alen) +``` + +**描述:** + +发送数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|fd | 客户端套接字| +|buf | UDP数据报缓存区(包含待发送数据)| +|len | UDP数据报的长度| +|flags | 调用操作方式(一般设置为0)| +|addr | 指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换)| +|alen | addr所指结构体的长度| + + +## 软件设计 + +**主要代码分析** + +```c +static void UDPClientTask(void) +{ + //连接Wifi + WifiConnect(WIFI_SSID,WIFI_PASSWORD); + + //创建socket + int sock_fd; + if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { + printf("socket is error\r\n"); + exit(1); + } + //printf("socket sock_fd=%d \r\n",sock_fd); + + struct sockaddr_in server_addr; + bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址 + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(_PROT_); + //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr); + int sin_size = sizeof(struct sockaddr_in); + + char str[IP_LEN]; + inet_ntop(AF_INET, &server_addr.sin_addr, str, sizeof(str)); + printf("udp server IP_addr: %s at PORT %d\n",str,ntohs(server_addr.sin_port)); + + const char sendbufs[3][10]={{"0000"},{"1111"},{"2222"}}; + struct sockaddr_in repaddr; + + for(int i=0; i<3; i++) + { + if(sendto(sock_fd, sendbufs[i], strlen(sendbufs[i])+1, 0,(struct sockaddr *)&server_addr, (socklen_t )sin_size)==-1) + { + printf("send error\n"); + close(sock_fd); + exit(-1); + } + printf("send smg: %s size: %d\n",sendbufs[i],strlen(sendbufs[i])); + + // + memset(&repaddr, 0, sizeof(struct sockaddr)); + int size = recvfrom(sock_fd, recvbuf, sizeof(recvbuf)-1, 0, (struct sockaddr *)&repaddr, (socklen_t *)&sin_size); + if(size>0) + { + printf("recv msg: %s \n",recvbuf); + } + usleep(500*1000); + } + + char send_buf[100]="exit"; + printf("send exit\n"); + int res = sendto(sock_fd, send_buf, strlen(send_buf)+1, 0,(struct sockaddr *)&server_addr, (socklen_t )sin_size); // 向服务器发送信息 + if(res==-1) + { + perror("sendto error\n"); + exit(-1); + } + usleep(500*1000); + close(sock_fd); +} +``` + +## 编译调试 + +### 修改对接热点的账号密码 + +修改`udp_client.c`第32行和33行的WiFi热点SSID和密码,改成自己环境中的WiFi热点。 + +``` +// 默认WiFi名和密码 +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" +``` + +### 修改服务端的ip地址和端口号 + +修改`udp_client.c`第27行和38行的端口号和ip地址,改成自己创建的服务端ip地址。 + +``` +// 默认服务端的ip地址和端口号 +#define _PROT_ 8800 +#define _SERVER_IP_ "192.168.1.114" +``` + +### 修改 BUILD.gn 文件 + +修改 `applications/app/BUILD.gn` 路径中的 BUILD.gn 文件,指定 `network_udpclient_demo` 参与编译。 + +``` + # "TW303_Network_mqttclient:network_mqttclient_example", + # "TW402_APP_oled_u8g2:app_oled_u8g2_example", + # "TW304_Network_tcpserver:network_tcpserver_demo", + # "TW304_Network_tcpclient:network_tcpclient_demo", + "TW305_Network_udpclient:network_udpclient_demo", +``` + +### 运行结果 + +示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,会打印连接到的Wifi热点信息,以及开启tcp服务端,等待客户端连接 + +链接wifi成功,并打印IP信息 +``` +callback function for wifi connect + +WaitConnectResult:wait success[1]s +WiFi connect succeed! +begain to dhcp<-- DHCP state:Inprogress --> + +<-- DHCP state:Inprogress --> + +<-- DHCP state:Inprogress --> + +<-- DHCP state:OK --> +server : + server_id : 192.168.1.1 + mask : 255.255.255.0, 1 + gw : 192.168.1.1 + T0 : 7200 + T1 : 3600 + T2 : 6300 +clients <1> : + mac_idx mac addr state lease tries rto + 0 b4c9b9af6afe 192.168.1.118 10 0 1 3 +``` + +链接tcp服务端并进行信息通信 +``` +udp server IP_addr: 192.168.1.112 at PORT 8800 +send smg: 0000 size: 4 + +recv msg: Hello! I'm Talkweb UDP Server! + +send smg: 1111 size: 4 +recv msg: Hello! I'm Talkweb UDP Server! + +send smg: 2222 size: 4 +recv msg: Hello! I'm Talkweb UDP Server! + +send exit +``` + + diff --git a/applications/app/TW305_Network_udpclient/include/wifi_connect.h b/applications/app/TW305_Network_udpclient/include/wifi_connect.h new file mode 100644 index 0000000000000000000000000000000000000000..c6bb2b6d2fd27b68327ad7105404f0ae89fafed6 --- /dev/null +++ b/applications/app/TW305_Network_udpclient/include/wifi_connect.h @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __WIFI_CONNECT_H__ +#define __WIFI_CONNECT_H__ + +int WifiConnect(const char *ssid,const char *psk); + +#endif /* __WIFI_CONNECT_H__ */ + + + diff --git a/applications/app/TW305_Network_udpclient/src/udp_client.c b/applications/app/TW305_Network_udpclient/src/udp_client.c new file mode 100644 index 0000000000000000000000000000000000000000..1a63e25cf5844b360053f65129cdfd1914134465 --- /dev/null +++ b/applications/app/TW305_Network_udpclient/src/udp_client.c @@ -0,0 +1,118 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "lwip/sockets.h" +#include "wifi_connect.h" + +#include +#include + +/*UDP SERVER 参数*/ +#define _PROT_ 8800 +#define _SERVER_IP_ "192.168.1.112" +#define IP_LEN 16 + +/*wifi参数配置*/ +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" + +/*接收缓存和数据*/ +char recvbuf[1460]; + +/*udp服务器任务*/ +static void UDPClientTask(void) +{ + //连接Wifi + WifiConnect(WIFI_SSID,WIFI_PASSWORD); + + //创建socket + int sock_fd; + if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { + printf("socket is error\r\n"); + exit(1); + } + //printf("socket sock_fd=%d \r\n",sock_fd); + + struct sockaddr_in server_addr; + bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址 + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(_PROT_); + //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr); + int sin_size = sizeof(struct sockaddr_in); + + char str[IP_LEN]; + inet_ntop(AF_INET, &server_addr.sin_addr, str, sizeof(str)); + printf("udp server IP_addr: %s at PORT %d\n",str,ntohs(server_addr.sin_port)); + + const char sendbufs[3][10]={{"0000"},{"1111"},{"2222"}}; + struct sockaddr_in repaddr; + + for(int i=0; i<3; i++) + { + if(sendto(sock_fd, sendbufs[i], strlen(sendbufs[i])+1, 0,(struct sockaddr *)&server_addr, (socklen_t )sin_size)==-1) + { + printf("send error\n"); + close(sock_fd); + exit(-1); + } + printf("send smg: %s size: %d\n",sendbufs[i],strlen(sendbufs[i])); + + // + memset(&repaddr, 0, sizeof(struct sockaddr)); + int size = recvfrom(sock_fd, recvbuf, sizeof(recvbuf)-1, 0, (struct sockaddr *)&repaddr, (socklen_t *)&sin_size); + if(size>0) + { + printf("recv msg: %s \n",recvbuf); + } + usleep(500*1000); + } + + char send_buf[100]="exit"; + printf("send exit\n"); + int res = sendto(sock_fd, send_buf, strlen(send_buf)+1, 0,(struct sockaddr *)&server_addr, (socklen_t )sin_size); // 向服务器发送信息 + if(res==-1) + { + perror("sendto error\n"); + exit(-1); + } + usleep(500*1000); + close(sock_fd); +} + +static void UDPClientDemo(void) +{ + osThreadAttr_t attr; + + attr.name = "TCPClientTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = 1024*4; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)UDPClientTask, NULL, &attr) == NULL) + { + printf("[UDPServerDemo] Falied to create UDPClientTask!\n"); + } +} + +APP_FEATURE_INIT(UDPClientDemo); \ No newline at end of file diff --git a/applications/app/TW305_Network_udpclient/src/wifi_connect.c b/applications/app/TW305_Network_udpclient/src/wifi_connect.c new file mode 100644 index 0000000000000000000000000000000000000000..1acd81a4bf5f9fb5a85f07d5635134e1f938a938 --- /dev/null +++ b/applications/app/TW305_Network_udpclient/src/wifi_connect.c @@ -0,0 +1,277 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" + +#include "lwip/netif.h" +#include "lwip/netifapi.h" +#include "lwip/ip4_addr.h" +#include "lwip/api_shell.h" + +#include "wifi_device.h" + +#define DEF_TIMEOUT 15 +#define ONE_SECOND 1 + +#define SELECT_WLAN_PORT "wlan0" +#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_PSK + +static int g_staScanSuccess = 0; +static int g_ConnectSuccess = 0; +static int ssid_count = 0; + +WifiErrorCode g_error; +WifiEvent g_wifiEventHandler = {0}; + +static void WiFiInit(void); +static void WaitSacnResult(void); +static int WaitConnectResult(void); +static void OnWifiScanStateChangedHandler(int state, int size); +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info); +static void OnHotspotStaJoinHandler(StationInfo *info); +static void OnHotspotStateChangedHandler(int state); +static void OnHotspotStaLeaveHandler(StationInfo *info); + +static void OnHotspotStaJoinHandler(StationInfo *info) +{ + (void)info; + printf("STA join AP\n"); + return; +} + +static void OnHotspotStaLeaveHandler(StationInfo *info) +{ + (void)info; + printf("HotspotStaLeave:info is null.\n"); + return; +} + +static void OnHotspotStateChangedHandler(int state) +{ + printf("HotspotStateChanged:state is %d.\n", state); + return; +} + +static void WiFiInit(void) +{ + printf("<--Wifi Init-->\r\n"); + g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler; + g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler; + g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler; + g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler; + g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler; + g_error = RegisterWifiEvent(&g_wifiEventHandler); + if (g_error != WIFI_SUCCESS) + { + printf("register wifi event fail!\r\n"); + } + else + { + printf("register wifi event succeed!\r\n"); + } +} + +static void OnWifiScanStateChangedHandler(int state, int size) +{ + (void)state; + if (size > 0) + { + ssid_count = size; + g_staScanSuccess = 1; + } + return; +} + +int WifiConnect(const char *ssid, const char *psk) +{ + WifiScanInfo *wifi_info = NULL; + unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT; + static struct netif *g_lwip_netif = NULL; + WifiDeviceConfig select_ap_config = {0}; + + osDelay(200); + printf("<--System Init-->\r\n"); + + // 初始化WIFI + WiFiInit(); + + if (EnableWifi() != WIFI_SUCCESS) + { + printf("EnableWifi failed, error = %d\n", g_error); + return -1; + } + + //判断WIFI是否激活 + if (IsWifiActive() == 0) + { + printf("Wifi station is not actived.\n"); + return -1; + } + + //分配空间,保存WiFi信息 + wifi_info = malloc(sizeof(WifiScanInfo) * WIFI_SCAN_HOTSPOT_LIMIT); + if (wifi_info == NULL) + { + printf("faild to create wifiscanInfo.\n"); + return -1; + } + + do + { + //重置标志位 + ssid_count = 0; + g_staScanSuccess = 0; + //开启wifi扫描 + Scan(); + + //等待扫描结果 + WaitSacnResult(); + + //获取扫描列表 + g_error = GetScanInfoList(wifi_info, &size); + } while (g_staScanSuccess != 1); + + //打印WiFi列表 + printf("********************\r\n"); + for (uint8_t i = 0; i < ssid_count; i++) + { + printf("no:%03d, ssid:%-30s, rssi:%5d\r\n", i + 1, wifi_info[i].ssid, wifi_info[i].rssi / 100); + } + printf("********************\r\n"); + + //插件指定的wifi是否存在 + for (uint8_t i = 0; i < ssid_count; i++) + { + if (strcmp(ssid, wifi_info[i].ssid) == 0) + { + int result; + printf("Select:%3d wireless, Waiting...\r\n", i + 1); + //拷贝要连接的热点信息 + strcpy(select_ap_config.ssid, wifi_info[i].ssid); + strcpy(select_ap_config.preSharedKey, psk); + select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE; + + if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS) + { + if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1) + { + printf("WiFi connect succeed!\r\n"); + g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT); + break; + } + } + } + + if (i == ssid_count - 1) + { + printf("ERROR: No wifi as expected\r\n"); + while (1) + osDelay(100); + } + } + + //启动DHCP + if (g_lwip_netif) + { + dhcp_start(g_lwip_netif); + printf("begain to dhcp"); + } + + //等待DHCP + for (;;) + { + if (dhcp_is_bound(g_lwip_netif) == ERR_OK) + { + printf("<-- DHCP state:OK -->\r\n"); + + //打印获取到的IP信息 + netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL); + break; + } + + printf("<-- DHCP state:Inprogress -->\r\n"); + osDelay(100); + } + + osDelay(100); + if (wifi_info != NULL) + { + free(wifi_info); + wifi_info = NULL; + } + return 0; +} + +static int WaitConnectResult(void) +{ + int ConnectTimeout = DEF_TIMEOUT; + while (ConnectTimeout > 0) + { + sleep(1); + ConnectTimeout--; + if (g_ConnectSuccess == 1) + { + printf("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - ConnectTimeout)); + break; + } + } + if (ConnectTimeout <= 0) + { + printf("WaitConnectResult:timeout!\n"); + return 0; + } + + return 1; +} + +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info) +{ + (void)info; + + if (state > 0) + { + g_ConnectSuccess = 1; + printf("callback function for wifi connect\r\n"); + } + else + { + printf("connect error,please check ssid and password\r\n"); + } + return; +} + +static void WaitSacnResult(void) +{ + int scanTimeout = DEF_TIMEOUT; + while (scanTimeout > 0) + { + sleep(ONE_SECOND); + scanTimeout--; + if (g_staScanSuccess == 1) + { + printf("WaitSacnResult: wait success [%d] s \n", (DEF_TIMEOUT - scanTimeout)); + break; + } + } + if (scanTimeout <= 0) + { + printf("WaitSacnResult:timeout!\n"); + } +} diff --git a/applications/app/TW305_Network_udpserver/BUILD.gn b/applications/app/TW305_Network_udpserver/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..1a4219a7abd271e27b863e891ec1ad78ce3f3dc6 --- /dev/null +++ b/applications/app/TW305_Network_udpserver/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Talkweb Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +static_library("network_udpserver_demo") { + sources = [ + "src/wifi_connect.c", + "src/udp_server.c", + ] + + cflags = [ "-Wno-unused-variable" ] + cflags += [ "-Wno-unused-but-set-variable" ] + + include_dirs = [ + "//foundation/communication/wifi_lite/interfaces/wifiservice" , + "include" + ] +} \ No newline at end of file diff --git a/applications/app/TW305_Network_udpserver/README.md b/applications/app/TW305_Network_udpserver/README.md new file mode 100644 index 0000000000000000000000000000000000000000..62ffcfaecb5c1b386875d853825b3f05648bec52 --- /dev/null +++ b/applications/app/TW305_Network_udpserver/README.md @@ -0,0 +1,401 @@ +# Niobe开发板UDP联网演示 + +本案例程序将演示怎么在拓维Niobe WiFi IoT Core开发板上编写一个创建UDP服务器的业务程序,实现开发板联网与UDP客户端数据通信。 + +## 简述 +UDP是用户数据包协议(UDP,User Datagram Protocol), 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。RFC 768 描述了 UDP。 + +Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情。面向连接的是 TCP,该协议几乎做了所有的事情 + +## UDP服务端编程步骤 +1、创建一个socket,用函数socket(); +2、设置socket属性,用函数setsockopt();* 可选 +3、绑定IP地址、端口等信息到socket上,用函数bind(); +4、循环接收/发送数据,用函数recvfrom()/sendto(); +5、关闭网络连接; + +## 特点 +1、UDP是无连接的,即发送数据之前不需要建立连接; +2、UDP使用尽最大努力交付,即不保证可靠交付; +3、UDP是面向报文的; +4、UDP支持一对一、一对多、多对一和多对多的交互通信等。 + +## 结构体详解 + +``` +struct sockaddr_in { + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + uint8_t sin_zero[8]; +}; +``` + +**描述:** + +套接字地址,存放地址和端口信息 + +**参数:** + +| 名字 | 描述 | +| ------ | -------- | +|sin_family| 指代协议族,在socket编程中只能是AF_INET| +|sin_port| 存储端口号(使用网络字节顺序)| +|sin_addr| 存储IP地址,使用in_addr这个数据结构| +|sin_zero| 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节| + +**示例代码如下:** + +``` + //服务端地址信息 + struct sockaddr_in server_sock; + bzero(&server_sock, sizeof(server_sock)); + server_sock.sin_family = AF_INET; + //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_sock.sin_addr); + server_sock.sin_port = htons(_PROT_); +``` + +------------------------------------ + +``` +typedef struct { + unsigned long fds_bits[FD_SETSIZE / 8 / sizeof(long)]; +} fd_set; +``` + +**描述:** + +文件描述符集合 + +**操作函数:** + +| 名字 | 描述 | +| ------ | -------- | +|void FD_CLR(int fd, fd_set *set)| 清除某一个被监视的文件描述符| +|int FD_ISSET(int fd, fd_set *set)|测试一个文件描述符是否是集合中的一员| +|void FD_SET(int fd, fd_set *set)| 添加一个文件描述符,将set中的某一位设置成1| +|void FD_ZERO(fd_set *set)| 清空集合中的文件描述符,将每一位都设置为0| + +**示例代码如下:** + +``` + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock_fd,&fds);//将sock_fd添加至fds + + if(FD_ISSET(sock_fd,&fds))//判断sock_fd是否在fds中 + ;// + +``` + +-------------------------------------------- +``` +struct sockaddr { + sa_family_t sa_family; + char sa_data[14]; +}; +``` +**描述:** + +通用的套接字地址,与sockaddr_in类似 + +**参数** + +| 名字 | 描述 | +| ------ | -------- | +|sa_family| 地址族| +|sa_data|14字节,包含套接字中的目标地址和端口信息 | + +## 函数详解 + +``` +int WifiConnect(const char *ssid, const char *psk) +``` + +**描述:** +初始化网络,并连接指定wifi + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| ssid | 指定wifi的账户名 | +| psk | 指定wifi的密码 | + +``` +int socket(int domain, int type, int protocol) +``` + +**描述:** + +创建套接字 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| domain | 地址族,也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6 | +|type | 数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)和SOCK_RAW(原始套接字)| +|protocol | 传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议| + +``` +int bind(int fd, const struct sockaddr *addr, socklen_t len) +``` + +**描述:** + +把用于通信的地址和端口绑定到 socket 上 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| fd | 需要绑定的socket | +|addr | 存放服务端用于通信的地址和端口| +|len | addr 结构体的大小| + + +``` +ssize_t recvfrom(int fd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict alen) +``` + +**描述:** + +接收数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|fd | 服务端的套接字| +|buf | UDP数据报缓存区(包含所接收的数据)| +|len | 缓冲区长度| +|flags | 调用操作方式(一般设置为0)| +|addr | 指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换)| +|alen | 指针,指向addr结构体长度值| + + +``` +ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t alen) +``` + +**描述:** + +发送数据 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|fd | 套接字| +|buf | UDP数据报缓存区(包含待发送数据)| +|len | UDP数据报的长度| +|flags | 调用操作方式(一般设置为0)| +|addr | 指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换)| +|alen | addr所指结构体的长度| + +``` +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +``` + +**描述:** + +IO多路复用 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +|nfds | 表示集合中所有文件描述符的范围| +|readfds | select监视的可读文件句柄集合| +|writefds | select监视的可写文件句柄集合| +|exceptfds | select监视的异常文件句柄集合| +|timeout | 本次select()的超时结束时间,NULL表示永久等待| + +## 软件设计 + +**主要代码分析** + +```c +static void UDPServerTaskWithSelect(void) +{ + //服务端地址信息 + struct sockaddr_in server_sock; + + int sin_size = sizeof(struct sockaddr_in); + //连接Wifi + if(WifiConnect(WIFI_SSID,WIFI_PASSWORD)!=0) + { + perror("Wifi connect error!"); + exit(1); + } + //in_addr_t localIpaddr = GetLocalIpaddr(); + uint32_t localIpaddr = htonl(GetLocalIpaddr()); + char strr[IP_LEN]; + inet_ntop(AF_INET, &server_sock.sin_addr, strr, sizeof(strr)); + printf("GetLocalIpaddr is %s \n",strr); + + //创建socket + printf("socket begin\n"); + int sock_fd=-1; + if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { + perror("socket is error\r\n"); + exit(1); + } + printf("sock_fd=%d\n",sock_fd); + + bzero(&server_sock, sizeof(server_sock)); + server_sock.sin_family = AF_INET; + //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_sock.sin_addr); //inet_addr("127.0.0.1"); + server_sock.sin_port = htons(_PROT_); + char str[IP_LEN]; + inet_ntop(AF_INET, &server_sock.sin_addr, str, sizeof(str)); + printf("udp server IP_addr: %s at PORT %d\n",str,ntohs(server_sock.sin_port)); + + //调用bind函数绑定socket和地址 + if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1) + { + perror("bind is error\r\n"); + exit(1); + } + + //printf("start select\r\n"); + fd_set fds; + FD_ZERO(&fds); + + while(true) + { + printf("select wait...\n"); + FD_SET(sock_fd,&fds); + int ret = select(sock_fd + 1, &fds, NULL, NULL, NULL); + if(ret<=0) + { + //超时等 + printf("[DISCOVERY]ret:%d\n", ret); + continue; + } + //printf("select\n"); + if(FD_ISSET(sock_fd,&fds)) + { + //客户端地址信息 + struct sockaddr_in repaddr; + memset(recvbuf,0,sizeof(recvbuf)); + memset(&repaddr, 0, sizeof(struct sockaddr)); + int res = recvfrom(sock_fd, recvbuf, sizeof(recvbuf)-1, 0, (struct sockaddr *)&repaddr, (socklen_t *)&sin_size); + //"exit"特殊字符串,表示客户端申请断开链接 + if(strlen(recvbuf)==4 && memcmp(recvbuf,"exit",5)==0) + { + printf("remote client close,msg is %s\n",recvbuf); + sendto(sock_fd, "exit", strlen("exit") + 1, 0, (struct sockaddr *)&repaddr, (socklen_t)sin_size); + } + else + { + printf("recv msg :%s\n",recvbuf); + if (sendto(sock_fd, udpSendBuf, strlen(udpSendBuf) + 1, 0, (struct sockaddr *)&repaddr, (socklen_t)sin_size) == -1) + { + perror("send error \r\n"); + } + else{ printf("send msg %s \n",udpSendBuf);} + } + osDelay(10); + FD_ZERO(&fds); + } + } + close(sock_fd); + FD_CLR(sock_fd,&fds); + sock_fd=-1; + return ; +} +``` + +## 编译调试 + +### 修改对接热点的账号密码 + +修改`udp_server.c`第34行和35行的WiFi热点SSID和密码,改成自己环境中的WiFi热点。 + +``` +// 默认WiFi名和密码 +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" +``` + +### 修改服务端的ip地址和端口号 + +修改`udp_server.c`第28行和30行的端口号和ip地址,改成自己链接上wifi后的ip地址,此处应该从wifi的sdk中获取本身的ip地址,但还未找到相应api。 + +``` +// 默认服务端的ip地址和端口号 +#define _PROT_ 8800 +#define _SERVER_IP_ "192.168.1.114" +``` + +### 修改 BUILD.gn 文件 + +修改 `applications/app/BUILD.gn` 路径中的 BUILD.gn 文件,指定 `network_udpserver_demo` 参与编译。 + +``` + # "TW303_Network_mqttclient:network_mqttclient_example", + # "TW402_APP_oled_u8g2:app_oled_u8g2_example", + # "TW304_Network_tcpserver:network_tcpserver_demo", + # "TW304_Network_tcpclient:network_tcpclient_demo", + # "TW305_Network_udpclient:network_udpclient_demo", + "TW305_Network_udpserver:network_udpserver_demo", +``` + +### 运行结果 + +示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,会打印连接到的Wifi热点信息,以及开启udp服务端,等待客户端连接 + +链接wifi成功,并打印IP信息 +``` +callback function for wifi connect + +WaitConnectResult:wait success[1]s +WiFi connect succeed! +begain to dhcp<-- DHCP state:Inprogress --> + +<-- DHCP state:OK --> +server : + server_id : 192.168.1.1 + mask : 255.255.255.0, 1 + gw : 192.168.1.1 + T0 : 7200 + T1 : 3600 + T2 : 6300 +clients <1> : + mac_idx mac addr state lease tries rto + 0 849dc22100d4 192.168.1.112 10 0 1 4 +``` + +开启tcp服务端,并进行监听等待 +``` +tcp server IP_addr: 192.168.1.112 at PORT 8800 +select wait... +``` + +有客户端接入并进行通信 +``` +recv msg :0000 +send msg Hello! I'm Talkweb UDP Server! + +select wait... + +recv msg :1111 +send msg Hello! I'm Talkweb UDP Server! + +select wait... + +recv msg :2222 +send msg Hello! I'm Talkweb UDP Server! + +select wait... + +remote client close,msg is exit + +select wait... + +``` diff --git a/applications/app/TW305_Network_udpserver/include/wifi_connect.h b/applications/app/TW305_Network_udpserver/include/wifi_connect.h new file mode 100644 index 0000000000000000000000000000000000000000..c6bb2b6d2fd27b68327ad7105404f0ae89fafed6 --- /dev/null +++ b/applications/app/TW305_Network_udpserver/include/wifi_connect.h @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __WIFI_CONNECT_H__ +#define __WIFI_CONNECT_H__ + +int WifiConnect(const char *ssid,const char *psk); + +#endif /* __WIFI_CONNECT_H__ */ + + + diff --git a/applications/app/TW305_Network_udpserver/src/udp_server.c b/applications/app/TW305_Network_udpserver/src/udp_server.c new file mode 100644 index 0000000000000000000000000000000000000000..75cd1ae9a576eb4491dcca31b82efeefcc0badac --- /dev/null +++ b/applications/app/TW305_Network_udpserver/src/udp_server.c @@ -0,0 +1,152 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "lwip/sockets.h" +#include "wifi_connect.h" + +#include +#include +#include + +/*TCP SERVER 参数*/ +#define _PROT_ 8800 +#define _SERVER_IP_ "192.168.1.112" +#define TCP_BACKLOG 5 +#define IP_LEN 16 + +/*wifi参数配置*/ +#define WIFI_SSID "aaa" +#define WIFI_PASSWORD "talkweb1996" + +/*接收缓存和数据*/ +char recvbuf[1460]; +char *udpSendBuf = "Hello! I'm Talkweb UDP Server!"; + +/*UDP服务器任务,采用select*/ +static void UDPServerTaskWithSelect(void) +{ + //服务端地址信息 + struct sockaddr_in server_sock; + + int sin_size = sizeof(struct sockaddr_in); + //连接Wifi + if(WifiConnect(WIFI_SSID,WIFI_PASSWORD)!=0) + { + perror("Wifi connect error!"); + exit(1); + } + //in_addr_t localIpaddr = GetLocalIpaddr(); + uint32_t localIpaddr = htonl(GetLocalIpaddr()); + char strr[IP_LEN]; + inet_ntop(AF_INET, &server_sock.sin_addr, strr, sizeof(strr)); + printf("GetLocalIpaddr is %s \n",strr); + + //创建socket + printf("socket begin\n"); + int sock_fd=-1; + if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { + perror("socket is error\r\n"); + exit(1); + } + printf("sock_fd=%d\n",sock_fd); + + bzero(&server_sock, sizeof(server_sock)); + server_sock.sin_family = AF_INET; + //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); + inet_pton(AF_INET, _SERVER_IP_, &server_sock.sin_addr); //inet_addr("127.0.0.1"); + server_sock.sin_port = htons(_PROT_); + char str[IP_LEN]; + inet_ntop(AF_INET, &server_sock.sin_addr, str, sizeof(str)); + printf("udp server IP_addr: %s at PORT %d\n",str,ntohs(server_sock.sin_port)); + + //调用bind函数绑定socket和地址 + if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1) + { + perror("bind is error\r\n"); + exit(1); + } + + //printf("start select\r\n"); + fd_set fds; + FD_ZERO(&fds); + + while(true) + { + printf("select wait...\n"); + FD_SET(sock_fd,&fds); + int ret = select(sock_fd + 1, &fds, NULL, NULL, NULL); + if(ret<=0) + { + //超时等 + printf("[DISCOVERY]ret:%d\n", ret); + continue; + } + //printf("select\n"); + if(FD_ISSET(sock_fd,&fds)) + { + //客户端地址信息 + struct sockaddr_in repaddr; + memset(recvbuf,0,sizeof(recvbuf)); + memset(&repaddr, 0, sizeof(struct sockaddr)); + int res = recvfrom(sock_fd, recvbuf, sizeof(recvbuf)-1, 0, (struct sockaddr *)&repaddr, (socklen_t *)&sin_size); + //"exit"特殊字符串,表示客户端申请断开链接 + if(strlen(recvbuf)==4 && memcmp(recvbuf,"exit",5)==0) + { + printf("remote client close,msg is %s\n",recvbuf); + sendto(sock_fd, "exit", strlen("exit") + 1, 0, (struct sockaddr *)&repaddr, (socklen_t)sin_size); + } + else + { + printf("recv msg :%s\n",recvbuf); + if (sendto(sock_fd, udpSendBuf, strlen(udpSendBuf) + 1, 0, (struct sockaddr *)&repaddr, (socklen_t)sin_size) == -1) + { + perror("send error \r\n"); + } + else{ printf("send msg %s \n",udpSendBuf);} + } + osDelay(10); + FD_ZERO(&fds); + } + } + close(sock_fd); + FD_CLR(sock_fd,&fds); + sock_fd=-1; + return ; +} + +static void UDPServerDemo(void) +{ + osThreadAttr_t attr; + + attr.name = "UDPServerTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = 1024*4; + attr.priority = osPriorityNormal; + + if (osThreadNew((osThreadFunc_t)UDPServerTaskWithSelect, NULL, &attr) == NULL) + { + printf("[UDPServerDemo] Falied to create UDPServerTask!\n"); + } +} + +APP_FEATURE_INIT(UDPServerDemo); \ No newline at end of file diff --git a/applications/app/TW305_Network_udpserver/src/wifi_connect.c b/applications/app/TW305_Network_udpserver/src/wifi_connect.c new file mode 100644 index 0000000000000000000000000000000000000000..a6b318a5c694a14df6084dc717c22da6dd0bb397 --- /dev/null +++ b/applications/app/TW305_Network_udpserver/src/wifi_connect.c @@ -0,0 +1,292 @@ +/* +* Copyright (c) 2021 Talkweb Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" + +#include "lwip/netif.h" +#include "lwip/netifapi.h" +#include "lwip/ip4_addr.h" +#include "lwip/api_shell.h" + +#include "wifi_device.h" + +#define DEF_TIMEOUT 15 +#define ONE_SECOND 1 + +#define SELECT_WLAN_PORT "wlan0" +#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_PSK + +static int g_staScanSuccess = 0; +static int g_ConnectSuccess = 0; +static int ssid_count = 0; + +WifiErrorCode g_error; +WifiEvent g_wifiEventHandler = {0}; + +static struct netif *g_lwip_netif = NULL; + +static void WiFiInit(void); +static void WaitSacnResult(void); +static int WaitConnectResult(void); +static void OnWifiScanStateChangedHandler(int state, int size); +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info); +static void OnHotspotStaJoinHandler(StationInfo *info); +static void OnHotspotStateChangedHandler(int state); +static void OnHotspotStaLeaveHandler(StationInfo *info); + +static void OnHotspotStaJoinHandler(StationInfo *info) +{ + (void)info; + printf("STA join AP\n"); + return; +} + +static void OnHotspotStaLeaveHandler(StationInfo *info) +{ + (void)info; + printf("HotspotStaLeave:info is null.\n"); + return; +} + +static void OnHotspotStateChangedHandler(int state) +{ + printf("HotspotStateChanged:state is %d.\n", state); + return; +} + +static void WiFiInit(void) +{ + printf("<--Wifi Init-->\r\n"); + g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler; + g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler; + g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler; + g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler; + g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler; + g_error = RegisterWifiEvent(&g_wifiEventHandler); + if (g_error != WIFI_SUCCESS) + { + printf("register wifi event fail!\r\n"); + } + else + { + printf("register wifi event succeed!\r\n"); + } +} + +static void OnWifiScanStateChangedHandler(int state, int size) +{ + (void)state; + if (size > 0) + { + ssid_count = size; + g_staScanSuccess = 1; + } + return; +} + +//改函数需在函数WifiConnect调用成功之后才能获取到正确的ip地址 +in_addr_t GetLocalIpaddr(void) +{ + return ip4_netif_get_local_ip(g_lwip_netif); + //if(g_lwip_netif) + //{ + // return g_lwip_netif->ip_addr.u_addr.ip4.addr; + //} + //else + //{ + // return INADDR_ANY; + //} +} + +int WifiConnect(const char *ssid, const char *psk) +{ + WifiScanInfo *wifi_info = NULL; + unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT; + WifiDeviceConfig select_ap_config = {0}; + + osDelay(200); + printf("<--System Init-->\r\n"); + + // 初始化WIFI + WiFiInit(); + + if (EnableWifi() != WIFI_SUCCESS) + { + printf("EnableWifi failed, error = %d\n", g_error); + return -1; + } + + //判断WIFI是否激活 + if (IsWifiActive() == 0) + { + printf("Wifi station is not actived.\n"); + return -1; + } + + //分配空间,保存WiFi信息 + wifi_info = malloc(sizeof(WifiScanInfo) * WIFI_SCAN_HOTSPOT_LIMIT); + if (wifi_info == NULL) + { + printf("faild to create wifiscanInfo.\n"); + return -1; + } + + do + { + //重置标志位 + ssid_count = 0; + g_staScanSuccess = 0; + //开启wifi扫描 + Scan(); + + //等待扫描结果 + WaitSacnResult(); + + //获取扫描列表 + g_error = GetScanInfoList(wifi_info, &size); + } while (g_staScanSuccess != 1); + + //打印WiFi列表 + printf("********************\r\n"); + for (uint8_t i = 0; i < ssid_count; i++) + { + printf("no:%03d, ssid:%-30s, rssi:%5d\r\n", i + 1, wifi_info[i].ssid, wifi_info[i].rssi / 100); + } + printf("********************\r\n"); + + //插件指定的wifi是否存在 + for (uint8_t i = 0; i < ssid_count; i++) + { + if (strcmp(ssid, wifi_info[i].ssid) == 0) + { + int result; + printf("Select:%3d wireless, Waiting...\r\n", i + 1); + //拷贝要连接的热点信息 + strcpy(select_ap_config.ssid, wifi_info[i].ssid); + strcpy(select_ap_config.preSharedKey, psk); + select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE; + + if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS) + { + if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1) + { + printf("WiFi connect succeed!\r\n"); + g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT); + break; + } + } + } + + if (i == ssid_count - 1) + { + printf("ERROR: No wifi as expected\r\n"); + while (1) + osDelay(100); + } + } + + //启动DHCP + if (g_lwip_netif) + { + dhcp_start(g_lwip_netif); + printf("begain to dhcp"); + } + + //等待DHCP + for (;;) + { + if (dhcp_is_bound(g_lwip_netif) == ERR_OK) + { + printf("<-- DHCP state:OK -->\r\n"); + + //打印获取到的IP信息 + netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL); + break; + } + + printf("<-- DHCP state:Inprogress -->\r\n"); + osDelay(100); + } + + osDelay(100); + if (wifi_info != NULL) + { + free(wifi_info); + wifi_info = NULL; + } + return 0; +} + +static int WaitConnectResult(void) +{ + int ConnectTimeout = DEF_TIMEOUT; + while (ConnectTimeout > 0) + { + sleep(1); + ConnectTimeout--; + if (g_ConnectSuccess == 1) + { + printf("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - ConnectTimeout)); + break; + } + } + if (ConnectTimeout <= 0) + { + printf("WaitConnectResult:timeout!\n"); + return 0; + } + + return 1; +} + +static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info) +{ + (void)info; + + if (state > 0) + { + g_ConnectSuccess = 1; + printf("callback function for wifi connect\r\n"); + } + else + { + printf("connect error,please check ssid and password\r\n"); + } + return; +} + +static void WaitSacnResult(void) +{ + int scanTimeout = DEF_TIMEOUT; + while (scanTimeout > 0) + { + sleep(ONE_SECOND); + scanTimeout--; + if (g_staScanSuccess == 1) + { + printf("WaitSacnResult: wait success [%d] s \n", (DEF_TIMEOUT - scanTimeout)); + break; + } + } + if (scanTimeout <= 0) + { + printf("WaitSacnResult:timeout!\n"); + } +}