• Home
Name Date Size #Lines LOC

..--

BUILD.gnD12-May-2024842 2219

README_zh.mdD12-May-20249.5 KiB290215

tcp_server.cD12-May-20243.7 KiB12695

README_zh.md

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