基于内容
进行之前需要完成下列内容:
- 配置开发板和Ubuntu在同一网段
- Linux 网络通信之TCP
- 掌握串口相关的系统编程
开始
准备的资源
资源 | 用途 | 地址 |
---|---|---|
串口调试助手 | 和开发板通信 | 下载 |
简单的介绍
功能:开发板作为智能网关中转客户端和服务器的信息,其中和客户端通信方式是串口,和服务器通信方式是TCP。
伪代码如下:
智能网关:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pid = fork();
if(pid){
pid_sub = fork();
if(pid_sub){
while(1){
if(接收到串口数据)
转发到TCP服务器
}
}
else{
while(1){
if(接收到UDP数据)
转发到TCP服务器
}
}
}
else{
while(1){
if(接收到TCP服务器数据)
转发给UDP或者串口
}
}
服务器:
1
2
3
4
5
6
7
8
9
10
11
12
13
pid = fork()
if(pid){
while(1){
if(控制台输入)
TCP发出
}
}
else{
while(1){
if(TCP读取到信息)
TCP发出
}
}
具体的操作
开发板准备2个串口,一个是标准输入输出(开发板Xu[T/R]XD2),一个是目标串口ttySAC3(开发板Xu[T/R]XD3)。如果只有一根串口线就先接第2组串口,设置程序开机启动;然后关机接第3组串口,再启动进行测试。注意串口尽量别热插拔。
编写服务器端程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
/*自定义socket api*/
#include "src/include/socket_api.h"
int main(int argc,char *argv[]){
pid_t pid;
int tcp_server_fd,tcp_client_fd;
char str[64],buf[64];
int read_len;
if((tcp_server_fd = tcp_init(argv[1],atoi(argv[2]),5)) < 0){
return -1;
}
tcp_client_fd = tcp_build(tcp_server_fd);
pid = fork();
if(pid == -1){
printf("fork failed\n");
return 1;
}
else if(pid){ //当前进程(父进程)输入直接发送信息
while(1){
memset(str,0,sizeof(str));
scanf("%s",str);
fflush(stdin);
if(strncmp(str,"exit",4) == 0){//输入exit退出
kill(pid,9);
break;
}
write(tcp_client_fd,str,strlen(str));
}
}
else{ //子进程,接收信息返回信息
while(1){
memset(buf, 0, sizeof(buf));
read_len = read(tcp_client_fd, buf, sizeof(buf));
if(read_len){
printf("socket data is %s!\n",buf);//至少加\n,否则有可能打印不出来
memset(str,0,sizeof(str));
sprintf(str,"RE:%s",buf);
write(tcp_client_fd,str,strlen(str));
}
}
}
close(tcp_client_fd);
close(tcp_server_fd);
return 0;
}
编写网关程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
//进程间通信:共享内存
#include <sys/shm.h>
/*自定义串口 api*/
#include "src/include/uart_api.h"
/*自定义socket api*/
#include "src/include/socket_api.h"
int main(int argc,char *argv[]){
pid_t pid,pid_sub;
//串口配置
char *uart3 = "/dev/ttySAC3";
int uart_fd;
if((uart_fd = open(uart3, O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s is failed",uart3);
return -1;
}
set_opt(uart_fd, 115200, 8, 'N', 1);
//UDP先收端配置
int udp_fd;
struct sockaddr_in servaddr,src_addr,*share;//src_addr记录来源地址,对应share共享内存
if((udp_fd = udp_init_firstrecv(servaddr,"0.0.0.0",55006)) < 0){
return -1;
}
//TCP客户机配置
int tcp_fd;
if((tcp_fd = tcp_request(argv[1],atoi(argv[2]))) < 0){
return -1;
}
//创建共享内存
int shmid = shmget((key_t)1234, sizeof(struct sockaddr_in), 0666|IPC_CREAT);
if(shmid == -1){
perror("shmget");
return -1;
}
//指向共享内存地址空间
share = shmat(shmid, 0, 0);
char uart_buff[1024],udp_buff[1024],tcp_buff[1024];
int read_len;
//三进程处理
pid = fork();
if(pid == -1){
perror("fork");
return -1;
}
else if(pid){ //进程0:转发串口和UDP数据到TCP服务器
pid_sub = fork();
if(pid_sub == -1){
perror("fork");
return -1;
}
else if(pid_sub){ //进程0.1 转发串口数据到TCP服务器
while(1){
//注意因为uart_fd有O_NDELAY属性,所以read不阻塞
memset(uart_buff, 0, sizeof(uart_buff));
read_len = read(uart_fd, uart_buff, sizeof(uart_buff));
if(read_len){
printf("len is %d!\n",read_len);
read_len = 0;
if(strncmp(uart_buff,"exit",4) == 0){//输入exit退出
kill(pid,9);
kill(pid_sub,9);
break;
}
memset(tcp_buff,0,sizeof(tcp_buff));
sprintf(tcp_buff,"sci:%s",uart_buff);
write(tcp_fd, tcp_buff, strlen(tcp_buff));
}
usleep(10000);//无阻塞属性的循环需要延时
}
}
else{ //进程0.2 转发UDP数据到TCP服务器
while(1){
memset(udp_buff, 0, sizeof(udp_buff));
read_len = udp_recv(udp_fd,udp_buff,sizeof(udp_buff),&src_addr);//接收udp信息
if(read_len){
memcpy(share, &src_addr, sizeof(src_addr));//记录来源信息到共享内存
if(strncmp(udp_buff,"exit",4) == 0){//输入exit退出
kill(pid,9);
kill(getppid(),9);
break;
}
memset(tcp_buff,0,sizeof(tcp_buff));
sprintf(tcp_buff,"udp:%s",udp_buff);
write(tcp_fd, tcp_buff, strlen(tcp_buff));
}
}
}
}
else{ //进程1,转发TCP服务器的数据到串口或UDP
while(1){
memset(tcp_buff, 0, sizeof(tcp_buff));
read_len = read(tcp_fd, tcp_buff, sizeof(tcp_buff));
if(read_len){
if(strncmp(tcp_buff,"RE:udp:",7) == 0){
memset(udp_buff,0,sizeof(udp_buff));
memcpy(udp_buff, tcp_buff, strlen(tcp_buff));
udp_send(udp_fd,udp_buff,share);
}
else{//默认转发串口
memset(uart_buff,0,sizeof(uart_buff));
memcpy(uart_buff,tcp_buff,strlen(tcp_buff));
write(uart_fd, uart_buff, strlen(uart_buff));
}
}
}
}
close(uart_fd);
close(udp_fd);
close(tcp_fd);
//把共享内存从当前进程中分离
if(shmdt(share) == -1)
{
fprintf(stderr, "shmdt failed\n");
return -1;
}
//删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
return -1;
}
return 0;
}
最终生成结果如下:
编译生成Ubuntu服务端程序命令:
gcc src/socket_api.c server.c -o server -static
编译生成开发板智能网关程序命令:
arm-none-linux-gnueabi-gcc src/uart_api.c src/socket_api.c gateway.c -o gateway -static
结果
-
将server拷贝到ubuntu上先执行。
-
将gateway拷贝到开发板上执行。
-
串口测试(开发板上SAC3)
串口助手发送“hello world!”返回“RE:sci:hello world!”
ubuntu发送“abcde”,串口助手显示“abcde”
-
UDP测试
手机udp app发送“hello”返回“RE:udp:hello”
ubuntu发送“RE:udp:abcde”,手机app显示“RE:udp:abcde”
附录:
api接口程序网络部分为自己重新整理,并配有详细的例程在注释中。
接口文件下载地址:点这里
接口文件 | 地址 |
---|---|
串口 | c文件 h头文件 |
TCP/UDP | c文件 h头文件 |