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 191 192在创建客户端后点击“连接”,在数据发送窗口输入要发送的数据,点击发送后服务端会回复固定消息,如下图所示,且开发板收到消息后会通过日志打印出来。 193 194``` 195start accept 196accept addr 197recv :Hello! BearPi-HM_nano TCP Server! 198``` 199 200