上位机软件.zip 点击下载
一.实验基本介绍
本次实验主要是通过搭建TCP并发服务器,支持多用户连接控制智能小车,客户端上位机通过连接上小车搭建好的TCP_control并发服务器。通过发送TCP协议数据控制小车的前进,后退,左转,右转,停止,左旋,右旋,以及前舵机的左中右控制,后面摄像头舵机任意角度的连续控制,还有七彩灯的控制,灭火,鸣笛,小车的加速,减速。以及树莓派智能小车通过丰富的传感器采集的数据实时的显示在我们的上位机上。也是通过TCP协议通信。
二.TCP通信模型的设计
服务器端:(被动接受请求)
socket //电话机
|
bind(ip+port) //绑定电话号码 绑定服务器自己的ip和port等待客户端连接。
|
listen //监听有人打电话进来
|
accept //接听电话
|
recv/send //通话过程
|
close //挂机
客户端:(主动发起连接)
socket //电话机
|
bind(ip+port) //绑定电话号码
|
connect //拨打电话
|
recv/send //通话过程
|
close //挂机
三.TCP常用函数讲解
创建流式套接字
int socket(int domain, int type, int protocol);
功能: 创建socket ,返回对应的文件描述符
参数:
@domain 域(通信的范围)
@type SOCK_STREAM 流式套接字: 有序可靠,面向连接字节流
SOCK_DGRAM 报文套接字:无连接的,不可靠的
SOCK_RAW 原始套接字: 可以访问一些低层的网络协议
@protocol 0表示默认的方式
SOCK_STREAM TCP
SOCK_DGRAM UDP
返回值:
成功 文件描述符
失败 -1 ,并设置errno
把服务器的ip和port和sockfd绑定
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:
绑定一个地址(ip+port)到一个socket 文件描述符上
参数:
@sockfd socket 函数获得的文件描述符
@addr 地址信息结构体
//通用结构体
struct sockaddr {
sa_family_t sa_family; //地址族
char sa_data[14]; //地址信息
}
//TCP/IP协议的地址结构
struct sockaddr_in {
sa_family_t sin_family; //协议簇
in_port_t sin_port; //端口
struct in_addr sin_addr; //ip地址
};
@addrlen 表示 addr 参数对应类型的地址信息结构体的大小
返回值:
成功 0
失败 -1&errno
操作:
(1).定义地址结构体变量,清零
struct sockaddr_in ser_addr;
memset(&ser_addr,0,sizeof(ser_addr));
(2).填充地址信息
ser_addr.sin_family = AF_INET;//地址协议族
ser_addr.sin_port = htons(8888);//端口号
ser_addr.sin_addr = inet_addr("192.168.1.7");
(3). 绑定
if(bind(sockfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr)) < 0)
{
perror("bind fail");
exit(EXIT_FAILURE);
}
获得客户连接请求,创建连接连接套接字,负责数据通信。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能: 获取连接请求,建立连接套接字
参数:
@sockfd 监听套接字
@addr 用来获取对端的地址信息
@addrlen 值结果参数(注意:必须自己初始化一个值sizeof(addr))
返回值:
成功 新连接的套接字文件描述符
失败 -1 ,并设置错误码。
----------------------------------------------------------------------------------------------------------------
特点:
1.请求队列中没有请求,阻塞调用者
2.每次提取一路请求,就会创建一个新的套接字,我们称为连接套接字,用来和客户端收发数据
-----------------------------------------------------------------------------------------------------------------
int listen(int sockfd ,int backlog);
功能:把同一个时刻来的套接字存放如监听队列中。<同一个时刻>
参数:
@sockfd 监听的套接字
@backlog 连接请求队列的最大长度。一般设置为30 以下。
注意:有的时候backlog很小,但是我们也可以了解很多台机器。服务器机器处理速度很快队列来不及填满就处理完了,而且在同一个时刻到来的连接还是很少的
客户端向服务器请求连接的函数
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:向服务端发起连接。
参数:
@sockfd 客户端的socket文件描述符
@addr 代表服务器端的地址信息
@addrlen addr 对应的地址结构体类型的大小
返回值:
成功返回0,失败返回-1
发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:给指定的sockfd发送数据
参数:
@scokfd 套接字
@ buf 发送数据的缓冲区
@len 要发送数据的长度
@flags 设置网络标志 ,常常设置为0
返回值:
成功,返回值 > 0,发送成功的字节数
返回值==0,发送超时
返回值<0, 发送失败,并设置错误码
//在阻塞模式下,下面的公式等价
注:send(sockfd, buf, len, 0);
接收数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:接收指定的sockfd的数据即连接套接字返回的文件描述符
参数:
@scokfd 套接字
@ buf 接受数据的缓冲区
@len 要接受数据的长度
@flags 设置网络标志 ,常常设置为0
返回值:
成功,返回值 > 0,成功接收的字节数
返回值==0,对方关闭了连接
返回值<0, 发送失败,并设置错误码
//在阻塞模式下,下面的公式等价
注:recv(sockfd, buf, len, 0);
四.TCP多线程并发服务器设计
多线程TCP并发服务器 。
//线程处理函数
void *do_client(void *age)
{
int connect_fd = *(int *)arg;
while(1)
{
//接收客户端数据
recv();..
}
}
socket(...);
bind(...);
listen(...);
while(1)
{
pconnect_fd = malloc(sizeof(int)); //在堆区创建空间保持文件描述符
*pconnect = accept(...);
//创建线程接收数据
ret = pthread_creat(&tind,NULL,do_client,pconnect_fd);
close(...);
exit(...);
}
pthread_detach(tid);
}
对于本次实验中我们也是采用了多线程的思维去处理,
一路线程负责解析上位机传过来的TCP数据,并执行相应的动作。
一路线程负责将小车传感器采集的数据打包传给上位机显示。
一路线程负责检测舵机的控制状态,并执行相应的舵机动作。
五.操作步骤
小车启动嘀嘀嘀三声之后会发出热点,名称Yahboom_Car密码12345678此时我们用电脑连接小车的热点。再通过putty登录到小车,IP地址填写192.168.50.1
这时输入账号 pi 密码yahboom进入树莓派
随后输入top
可以看到541进程号是蓝牙遥控的进程,554是摄像头进程,注意这里的进程号不同的树莓派是不同的,然后我们要先关闭蓝牙进程这样我们打开TCP进程才不会与蓝牙进程冲突。
输入命令杀掉进程:注意上个界面如何退出呢,输入ctrl+z退出。
命令:sudo kill -9 541
如下图所示:
然后进到SmartCar目录下:
找到TCP_control.c 里面有个ip地址和端口号可以根据树莓派自己的ip进行修改,一般出厂是自带热点所以ip【192.168.50.1】(注意:树莓派4B最新版本采用50网段,老的客户可能是0网段),端口号默认是【8888】
修改完毕一定要编译程序:
gcc TCP_control.c -o TCP_control -lwiringPi -lpthread
编译完成后,
我们直接运行TCP进程,如下命令:
./TCP_control
这样我们的TCP进程就运行成功了。
如下提示Listen:
下面我们打开上位机选择 4WD-树莓派。
然后点击【连接视频】则可以看到视频,点击【连接控制】则其他所有按键都可以控制。
(注意:树莓派4B最新版本采用50网段192.168.50.1,老的客户可能是0网段192.168.0.1)
成功后如下:
服务器会有如下提示:
至此TCP控制就算完成。