1# Niobe407开发板TCP联网演示 2 3本案例程序将演示怎么在拓维Niobe407开发板上编写一个创建tcp服务器的业务程序,实现开发板联网与tcp客户端数据通信。 4 5## 简述 6传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。 7 8TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。TCP假设它可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从硬线连接到分组交换或电路交换网络的各种通信系统之上操作。 9 10## TCP编程步骤 11 1、创建一个socket,用函数socket(); 12 2、设置socket属性,用函数setsockopt(); * 可选 13 3、绑定IP地址、端口等信息到socket上,用函数bind(); 14 4、开启监听,用函数listen(); 15 5、接收客户端上来的连接,用函数accept(); 16 6、收发数据,用函数send()和recv(),或者read()和write(); 17 7、关闭网络连接; 18 8、关闭监听 19 20## 特点 21(1)基于流的方式; 22(2)面向连接; 23(3)可靠通信方式; 24(4)在网络状况不佳的时候尽量降低系统由于重传带来的带宽开销; 25(5)通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点 26 27## 结构体详解 28 29``` 30struct sockaddr_in { 31 sa_family_t sin_family; 32 in_port_t sin_port; 33 struct in_addr sin_addr; 34 uint8_t sin_zero[8]; 35}; 36``` 37 38**描述:** 39 40地址和端口信息 41 42**参数:** 43 44| 名字 | 描述 | 45| ------ | -------- | 46|sin_family| 指代协议族,在socket编程中只能是AF_INET| 47|sin_port| 存储端口号(使用网络字节顺序)| 48|sin_addr| 存储IP地址,使用in_addr这个数据结构| 49|sin_zero| 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节| 50 51**示例代码如下:** 52 53``` 54 //服务端地址信息 55 struct sockaddr_in server_sock; 56 bzero(&server_sock, sizeof(server_sock)); 57 server_sock.sin_family = AF_INET; 58 //server_sock.sin_addr.s_addr = htonl(INADDR_ANY); 59 inet_pton(AF_INET, "192.168.1.121", &server_sock.sin_addr); 60 server_sock.sin_port = htons(_PROT_); 61``` 62 63------------------------------------ 64 65``` 66typedef struct { 67 unsigned long fds_bits[FD_SETSIZE / 8 / sizeof(long)]; 68} fd_set; 69``` 70 71**描述:** 72 73文件描述符集合 74 75**操作函数:** 76 77| 名字 | 描述 | 78| ------ | -------- | 79|void FD_CLR(int fd, fd_set *set)| 清除某一个被监视的文件描述符| 80|int FD_ISSET(int fd, fd_set *set)|测试一个文件描述符是否是集合中的一员| 81|void FD_SET(int fd, fd_set *set)| 添加一个文件描述符,将set中的某一位设置成1| 82|void FD_ZERO(fd_set *set)| 清空集合中的文件描述符,将每一位都设置为0| 83 84**示例代码如下:** 85 86``` 87 fd_set fds; 88 FD_ZERO(&fds); 89 FD_SET(sock_fd,&fds);//将sock_fd添加至fds 90 91 if(FD_ISSET(sock_fd,&fds))//判断sock_fd是否在fds中 92 ;// 93 94``` 95 96## 函数详解 97 98### void tcp_server_example(void) 99 100 调用此函数初始化ETH接口,并注册eth_enable_state_callBack回调函数 101 102 函数原型: 103 104 void tcp_client_example(void) 105 { 106 ethernet_enable(eth_enable_state_callBack); //有线网络使能 107 } 108 109 110### static void eth_enable_state_callBack(EthLinkState state) 111 112 此函数为ETH状态回调函数,在ETH初始化的时候被注册。 113 114 state:当前ETH连接状态 115 116 函数原型: 117 118 static void eth_enable_state_callBack(EthLinkState state) 119 { 120 static int net_init_finish = 0; 121 /* ETH连接断开*/ 122 if(state == STATE_UPDATE_LINK_DOWN){ 123 printf("ETH LINK STATE: DisConnected!\r\n"); 124 } 125 /* ETH连接成功*/ 126 else if(state == STATE_UPDATE_LINK_UP){ 127 printf("ETH LINK STATE: Connected!\r\n"); 128 if(net_init_finish == 0) 129 { 130 osThreadAttr_t attr; 131 attr.name = "tcp_server"; 132 attr.attr_bits = 0U; 133 attr.cb_mem = NULL; 134 attr.cb_size = 0U; 135 attr.stack_mem = NULL; 136 attr.stack_size = 1024 * 4; 137 attr.priority = 25; 138 tcp_server_id = osThreadNew((osThreadFunc_t)tcp_server, NULL, &attr); 139 if (tcp_server_id == NULL) 140 { 141 printf("Failed to create tcp_server thread!\n"); 142 } 143 net_init_finish = 1; 144 } 145 } 146 } 147 148 函数实现功能: 当ETH连接状态发生改变时,回调此函数。 149 150 当state == STATE_UPDATE_LINK_UP时,表示连接成功,此时创建1个TCPClient线程, 且将net_init_finish = 1。 如此, 当ETH状态断开再连接时,将不会再次创建TCPServer线程 151 152 当state == STATE_UPDATE_LINK_DOWN时,表示连接断开 153 154### void tcp_server(void *argument) 155 156 此函数为TCPServer初始化及数据收发任务函数 157 158 argument:线程传入参数,这里未使用 159 160 实现功能: 实现TCP数据接收后,原数据返回的功能 161 162 函数原型: 163 164 void tcp_server(void *argument) 165 { 166 (void)argument; 167 int sock = -1,connected; 168 char recv_data[512]={0}; 169 struct sockaddr_in server_addr,client_addr; 170 socklen_t sin_size; 171 int recv_data_len; 172 173 printf("start tcp_server test\r\n"); 174 /* 申请套接字,本质是netconn_new函数的封装 */ 175 sock = socket(AF_INET, SOCK_STREAM, 0); 176 if (sock < 0){ 177 printf("Socket error\n"); 178 goto __exit; 179 } 180 /* 为sockaddr_in结构体成员赋值,用于以下的bind绑定 */ 181 server_addr.sin_family = AF_INET; 182 server_addr.sin_addr.s_addr = INADDR_ANY; 183 server_addr.sin_port = htons(SERVER_LISTEN_PORT); 184 /* 清空sockaddr_in结构体内存空间 */ 185 memset_s(&(server_addr.sin_zero), sizeof(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); 186 187 /* 服务器绑定ip地址与端口 */ 188 if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) 189 { 190 printf("Unable to bind\n"); 191 goto __exit; 192 } 193 194 /* 服务器进入监听状态 */ 195 if (listen(sock, 5) == -1) 196 { 197 printf("Listen error\n"); 198 goto __exit; 199 } 200 201 while(1) 202 { 203 sin_size = sizeof(struct sockaddr_in); 204 /* 等待远端client的链接 */ 205 connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size); 206 207 printf("new client connected from (%s, %d)\n", 208 inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); 209 { 210 int flag = 1; 211 /* 设置套接字的选项 */ 212 setsockopt(connected, 213 IPPROTO_TCP, /* set option at TCP level */ 214 TCP_NODELAY, /* name of option */ 215 (void *) &flag, /* the cast is historical cruft */ 216 sizeof(int)); /* length of option value */ 217 } 218 219 while(1) 220 { 221 /* 成功接收到数据,返回接收的数据长度 */ 222 recv_data_len = recv(connected, recv_data, 511, 0); 223 224 if (recv_data_len <= 0) 225 break; 226 else 227 recv_data[recv_data_len] = '\0'; 228 229 printf("recv %s\n",recv_data); 230 231 /* 发送数据内容 */ 232 write(connected,recv_data,recv_data_len); 233 234 osDelay(200); 235 } 236 if (connected >= 0) 237 closesocket(connected); 238 239 connected = -1; 240 } 241 __exit: 242 if (sock >= 0) closesocket(sock); 243 if (recv_data) free(recv_data); 244 } 245 246 其中: 247 248 server_addr.sin_addr.s_addr = INADDR_ANY 表示连接所有IP的TCPClient 249 250 SERVER_LISTEN_PORT 表示服务器监听的端口号 251 252 253## 编译调试 254- 进入//kernel/liteos_m目录, 在menuconfig配置中进入如下选项: 255 256 `(Top) → Platform → Board Selection → select board niobe407 → use talkweb niobe407 application → niobe407 application choose` 257 258- 选择 `302_network_tcpserver` 259 260- 回到sdk根目录,执行`hb build -f`脚本进行编译。 261 262### 运行结果 263 264示例代码编译烧录代码后,按下开发板的RESET按键,使用电脑客户端连接设备服务器。 265 266电脑端TCP客户端发送消息: 267 "hello,this is device tcp_service test!" 268 269设备端TCP服务器接收消息: 270 271 ETH Init Success! 272 ETH LINK STATE: Connected! 273 start tcp_server test 274 new client connected from (192.168.8.119, 64333) 275 recv hello,this is device tcp_service test! 276 277 recv hello,this is device tcp_service test! 278 279 recv hello,this is device tcp_service test! 280 281 recv hello,this is device tcp_service test! 282 283 recv hello,this is device tcp_service test! 284 285 recv hello,this is device tcp_service test! 286 287 recv hello,this is device tcp_service test! 288 289 recv hello,this is device tcp_service test! 290