本人是专科,然后专升本考上了本科。现在到了毕业的时候了,毕业设计选的是基于树莓派的智能避障小车的设计与实现。一个软硬件都有的设计…
在设计过程中我用到了两个在学校学不到的技术,一个是C语言的网络通讯,一个是多线程。
C语言网络通讯用的UDP,当时是别人的代码复制运行…“牛逼”,自己比着葫芦画瓢…“为啥不行??”,然后一顿自闭。
虽然我现在也是半吊子水平,但是想记录一下自己的知识。
首先,我用的是Linux系统进行编程,所以需要先包含头文件:#include<sys/socket.h>
之后创建socket文件描述符int sockfd=socket(AF_INET,SOCK_DGRAM,0);
文件描述符:文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作(包括网络socket操作)的系统调用都通过文件描述符。
程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。
第一个参数是IPV4还是IPV6:
AF_INET : IPv4协议;
AF_INET6: IPv6协议;
AF_LOCAL: Unix域协议;
AF_ROUTE:路由套接口;
AF_KEY :密钥套接口。
第二个参数是TCP还是UDP通讯:
SOCK_STREAM:字节流套接字(TCP);
SOCK_DGRAM : 数据报套接字(UDP);
SOCK_RAW : 原始套接口。
procotol: 使用的特定协议,一般使用默认协议(NULL)。
第三个参数一般为0.
之后struct sockaddr_in addr,cli;
其中struct sockaddr_in是声明一个sockaddr_in的结构体,其内容是:
struct sockaddr_in{
sa_family_t sin_family; //地址族,常用AF_INET
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8] //不使用
};
struct in_addr{
in_addr_t s_addr; //32位IPV4地址
}
所以之后就是给本地addr数据结构赋值
bzero(&addr,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port =htons(port);
addr.sin_addr.s_addr=INADDR_ANY;
bzero(&addr,sizeof(addr));是对addr这个结构体都初始化为0;
port为端口号,INADDR_ANY:转换过来就是0.0.0.0,泛指本机的意思。
然后是绑定,将文件描述符与addr数据结构绑定
int ret =bind(sockfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
首先第一个参数是socket文件描述符
第二个是将addr强制类型转换为struct sockaddr*,具体原因请点击
第三个是输入struct sockaddr_in的空间大小。
bind()成功,返回0;出错,返回-1
之后就可以通过recvfrom()和sendto()进行信息的收发了。
int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);
返回值说明:
成功则返回实际接收到的字符数,失败返回-1,错误原因会存于errno 中。
参数说明:
s: socket描述符;
buf: UDP数据报缓存区(包含所接收的数据);
len: 缓冲区长度。
flags: 调用操作方式(一般设置为0)。
from: 指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换);
fromlen:指针,指向from结构体长度值。
示例:
char buff[data_in_len];
socklen_t len=sizeof(cli);
recvfrom(sockfd,buff,sizeof(buff),0,(struct sockaddr*)&cli,&len);
socklen_t相当于int。
之后是sendto()发送:
int sendto(int s, const void *buf, int len, unsigned int flags,const struct sockaddr *to, int tolen);
返回值说明:
成功则返回实际传送出去的字符数,失败返回-1,错误原因会存于errno 中。
参数说明:
s: socket描述符;
buf: UDP数据报缓存区(包含待发送数据);
len: UDP数据报的长度;
flags:调用方式标志位(一般设置为0);
to: 指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换);
tolen:to所指结构体的长度;
示例:
char buff[data_out_len];
sendto(sockfd,buff,sizeof(buff),0,(struct sockaddr*)&cli,len);
至此,写完了,感觉自己还是菜emmm
下面上我那小车的代码,里面还有多线程的东西
###很菜,勿喷
#include <stdio.h>
#include<string>
#include<iostream>
#include<pthread.h>
#include<sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
bool log=0;
typedef struct
{
int sockfd;
struct sockaddr_in cli;
char *fin;
char *fout;
}Data;
#define port 10000
using namespace std;
void* runfin(void *arg){
Data *data=(Data*)arg;
socklen_t len=sizeof(data->cli);
char buff[data_in_len];
struct sockaddr_in temp_cli;
cout<<"准备接收:\n";
recvfrom(data->sockfd,buff,sizeof(buff),0,(struct sockaddr*)&(data->cli),&len);
log=1;
while(1){
pthread_mutex_lock(&indata_mutex);
strcpy(data->fin,buff);
pthread_mutex_unlock(&indata_mutex);
recvfrom(data->sockfd,buff,sizeof(buff),0,NULL,&len);
}
return NULL;
}
void* runfout(void *arg){
Data *data=(Data*)arg;
socklen_t len=sizeof(data->cli);
char buff[data_out_len];
while(1)
{
sleep(1);
if(log)
{
pthread_mutex_lock(&outdata_mutex);
strcpy(buff,data->fout);
pthread_mutex_unlock(&outdata_mutex);
sendto(data->sockfd,buff,sizeof(buff),0,(struct sockaddr*)&(data->cli),len);
}
}
return NULL;
}
int feedback(char *fin,char *fout){
pthread_t Fin,Fout;
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
struct sockaddr_in addr,cli;
bzero(&addr,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port =htons(port);
addr.sin_addr.s_addr=INADDR_ANY;
int ret =bind(sockfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
if(0>ret)
{
printf("bind\n");
return -1;
}
Data *data=(Data*)malloc(sizeof(Data));
data->sockfd=sockfd;
data->fin=fin;
data->fout=fout;
bzero(&data->cli,sizeof(data->cli));
socklen_t len=sizeof(data->cli);
pthread_create(&Fin,NULL,runfin,(void*)data);
pthread_create(&Fout,NULL,runfout,(void*)data);
//pthread_join(Fin,NULL);
//pthread_join(Fout,NULL);
return 0;
}
/*int main()
{
char fin[data_len]={"523"};
char fout[data_len]={"123\n"};
feedback(fin,fout);
return 0;
}*/
这里面的发送会一直循环…毕竟要实时获得信息。这只是其中一个头文件的代码,还有控制端和PWM调速的没发。