• Home
Name Date Size #Lines LOC

..--

wifi/12-May-2024-257189

BUILD.gnD12-May-2024911 2723

README.mdD12-May-20246.3 KiB200152

tcp_server_demo.cD12-May-20243.7 KiB13082

README.md

1# BearPi-HM_Nano开发板WiFi编程开发——TCP服务器
2本示例将演示如何在BearPi-HM_Nano开发板上使用socket编程创建TCP服务端,接收客户端消息并回复固定消息。
3
4
5## socket API分析
6本案例主要使用了以下几个API完socket编程实验。
7### socket()
8
9```c
10sock_fd = socket(AF_INET, SOCK_STREAM, 0)) //AF_INT:ipv4, SOCK_STREAM:tcp协议
11```
12**描述:**
13
14在网络编程中所需要进行的第一件事情就是创建一个socket,无论是客户端还是服务器端,都需要创建一个socket,该函数返回socket文件描述符,类似于文件描述符。socket是一个结构体,被创建在内核中。
15### bind()
16```c
17bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))
18```
19**描述:**
20
21把一个本地协议地址和套接口绑定,比如把本机的2222端口绑定到套接口。注意:为什么在上图中客户端不需要调用bind函数?这是因为如果没有调用bind函数绑定一个端口的话,当调用connect函数时,内核会为该套接口临时选定一个端口,因此可以不用绑定。而服务器之所以需要绑定的原因就是,所以客户端都需要知道服务器使用的哪个端口,所以需要提前绑定。
22
23
24### listen()
25```c
26int listen(int s, int backlog)
27```
28**描述:**
29
30当socket创建后,它通常被默认为是主动套接口,也就是说是默认为要马上调用connect函数的,而作为服务器是需要被动接受的,所以需要调用linsten函数将主动套接口转换成被动套接口。调用linsten函数后,内核将从该套接口接收连接请求。
31
32
33
34### accept()
35```c
36int accept(s, addr, addrlen)
37```
38**描述:**
39
40此函数返回已经握手完成的连接的套接口。注意:此处的套接口不同于服务器开始创建的监听套接口,此套接口是已经完成连接的套接口,监听套接口只是用来监听。
41
42### recv()
43```c
44int recv( SOCKET s, char *buf, int  len, int flags)
45```
46**描述:**
47
48recv函数用来从TCP连接的另一端接收数据。
49
50### send()
51```c
52int send( SOCKET s,char *buf,int len,int flags )
53```
54**描述:**
55send函数用来向TCP连接的另一端发送数据。
56
57
58
59
60## 软件设计
61
62**主要代码分析**
63
64完成Wifi热点的连接需要以下几步。
65
661. 通过 `socket` 接口创建一个socket,`AF_INT`表示ipv4,`SOCK_STREAM`表示使用tcp协议。
672. 调用 `bind` 接口绑定socket和地址。
683. 调用 `listen` 接口监听(指定port监听),通知操作系统区接受来自客户端链接请求,第二个参数:指定队列长度。
694. 调用`accept`接口从队列中获得一个客户端的请求链接。
705. 调用 `recv` 接口接收客户端发来的数据。
716. 调用 `send` 接口向客户端回复固定的数据。
72
73```c
74static void TCPServerTask(void)
75{
76    //在sock_fd 进行监听,在 new_fd 接收新的链接
77    int sock_fd, new_fd;
78
79    //服务端地址信息
80    struct sockaddr_in server_sock;
81
82    //客户端地址信息
83    struct sockaddr_in client_sock;
84    int sin_size;
85
86    struct sockaddr_in *cli_addr;
87
88    //连接Wifi
89    WifiConnect(CONFIG_WIFI_SSID, CONFIG_WIFI_PWD);
90
91    //创建socket
92    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
93        perror("socket is error\r\n");
94        exit(1);
95    }
96
97    bzero(&server_sock, sizeof(server_sock));
98    server_sock.sin_family = AF_INET;
99    server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
100    server_sock.sin_port = htons(CONFIG_CLIENT_PORT);
101
102    //调用bind函数绑定socket和地址
103    if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1) {
104        exit(1);
105    }
106
107    //调用listen函数监听(指定port监听)
108    if (listen(sock_fd, TCP_BACKLOG) == -1) {
109        exit(1);
110    }
111
112    printf("start accept\n");
113
114    //调用accept函数从队列中
115    while (1) {
116        sin_size = sizeof(struct sockaddr_in);
117
118        if ((new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size)) == -1) {
119            perror("accept");
120            continue;
121        }
122
123        cli_addr = malloc(sizeof(struct sockaddr));
124
125        printf("accept addr\r\n");
126
127        if (cli_addr != NULL) {
128            int ret = memcpy_s(cli_addr, sizeof(cli_addr), &client_sock, sizeof(struct sockaddr));
129            if (!ret) {
130                perror("memcpy is error\r\n");
131                exit(1);
132            }
133        }
134        //处理目标
135        ssize_t ret;
136
137        while (1) {
138            memset_s(recvbuf, sizeof(recvbuf), 0, sizeof(recvbuf));
139            if ((ret = recv(new_fd, recvbuf, sizeof(recvbuf), 0)) == -1) {
140                printf("recv error \r\n");
141            }
142            printf("recv :%s\r\n", recvbuf);
143            sleep(TASK_DELAY_2S);
144            if ((ret = send(new_fd, buf, strlen(buf) + 1, 0)) == -1) {
145                perror("send : ");
146            }
147            sleep(TASK_DELAY_2S);
148        }
149        close(new_fd);
150    }
151}
152```
153
154## 编译调试
155
156### 修改 BUILD.gn 文件
157
158修改 `device\bearpi\bearpi_hm_nano\app` 路径下 BUILD.gn 文件,指定 `tcp_server` 参与编译。
159```r
160#"D1_iot_wifi_sta:wifi_sta",
161#"D2_iot_wifi_sta_connect:wifi_sta_connect",
162#"D3_iot_udp_client:udp_client",
163"D4_iot_tcp_server:tcp_server",
164#"D5_iot_mqtt:iot_mqtt",
165#"D6_iot_cloud_oc:oc_mqtt",
166```
167
168
169### 运行结果
170
171示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,会打印模块的本地IP,如本例程中的 `192.168.0.164` ,并开始准备获取客户端的请求链接。
172```
173g_connected: 1
174netifapi_dhcp_start: 0
175server :
176        server_id : 192.168.0.1
177        mask : 255.255.255.0, 1
178        gw : 192.168.0.1
179        T0 : 7200
180        T1 : 3600
181        T2 : 6300
182clients <1> :
183        mac_idx mac             addr            state   lease   tries   rto
184        0       181131a48f7a    192.168.0.164   10      0       1       2
185netifapi_netif_common: 0
186start accept
187```
188使用 Socket tool 创建客户端用于测试,如下图所示。
189
190![创建TCP_Clien](../../docs/figures/D4_iot_tcp_server/创建TCP_Clien.png)
191
192在创建客户端后点击“连接”,在数据发送窗口输入要发送的数据,点击发送后服务端会回复固定消息,如下图所示,且开发板收到消息后会通过日志打印出来。
193
194```
195start accept
196accept addr
197recv :Hello! BearPi-HM_nano TCP Server!
198```
199
200![TCP发送数据](../../docs/figures/D4_iot_tcp_server/TCP发送数据.png)