udp 是基于tcp/ip协议的无连接通讯。我基于zynq 7000建立了petalinux 系统,希望能有高效有效的通讯方式。以前做了tcp 基于连接的通讯实验,但实际中包会被拆开,感觉有点不好,所以特做了无连接的通讯测试,这就是udp 通讯。
1:实验过程
首先在网上查找了udp 在linux下的c 语言通讯程序。 但我需要运行的环境是zynq 7000上建立的petalinux2018.2 环境。我的环境是在ubuntu16,下安装 petalinux 2018.2实现的。采用交叉编译,petalinux上的udp服务器程序可以运行,可以接收到包,但解析不到地址。这样就不能实现通讯。我也怀疑测试的客户端程序有问题,所以也就写了一个客户端测试程序。
这样还是不行,我就在ubuntu16 下直接cc 这2个程序,发现可以实现通讯。就是在petalinux下运行服务端或客服端就得不到地址。这样在xilinx 论坛上提了此问题,后来终于得到一个提示,修改后就可以各种条件下都可以udp 通讯。
通讯的实验是:客户端程序udpc 发送一个包, 服务端udps得到这个包,显示,并回送一个包,客户端接收到,并显示。这样就验证了通讯。
2:服务端udps.c 的代码
在ubuntu 上运行编译是 :cc udps.c -o udps
在petalinux 上运行,在ubuntu 上运行交叉编译是 :arm-linux-gnueabihf-gcc udps.c -o udps
// Server side implementation of UDP client-server model
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8080
#define MAXLINE 1024
// Driver code
int main(int argc,char** argv) {
int sockfd;
char buffer[MAXLINE];
char *hello = "Hello from server";
struct sockaddr_in servaddr, cliaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
//int sport;
if(argc>1)
{
int sport;
sscanf(argv[1],"%d",&sport);
printf("port is %d\n",sport);
servaddr.sin_port=htons(sport);
}
// Bind the socket with the server address
if ( bind(sockfd, (const struct sockaddr *)&servaddr,
sizeof(servaddr)) < 0 )
{
perror("bind failed");
exit(EXIT_FAILURE);
}
int len, n,cport;
char addr_buffer[20];
len=sizeof(struct sockaddr);
printf("before recvfrom len =%d\n",len);
n = recvfrom(sockfd, (char *)buffer, MAXLINE,
MSG_WAITALL, ( struct sockaddr *) &cliaddr,
&len);
buffer[n] = '\0';
printf("Client : %s\n", buffer);
inet_ntop(AF_INET,&(cliaddr.sin_addr.s_addr),addr_buffer,len);
cport=ntohs(cliaddr.sin_port);
printf("caddr=%x\n",cliaddr.sin_addr.s_addr);
printf("addr=%s, len=%d\n",addr_buffer,len);
printf("c family=%d\n",cliaddr.sin_family);
printf("c port=%d and=%d\n",cliaddr.sin_port,cport);
sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &cliaddr,
len);
printf("Hello message sent.\n");
//check the server info
inet_ntop(AF_INET,&(servaddr.sin_addr),addr_buffer,len);
cport=ntohs(servaddr.sin_port);
printf("check server addr=%s, port=%d\n",addr_buffer,cport);
return 0;
}
程序的结构是:
建立socket 文件号,填充服务端信息,缺省端口号是8080,也可以命令行改变,argv[1],
./udps 8000
绑定监听端口号,等待客户端,接收信息,显示接收内容,包括客户信息,回送客户信息,关闭文件号,退出。
3: 客户端udpc.c 代码
在ubuntu 上运行编译是 :cc udpc.c -o udpc
在petalinux 上运行,在ubuntu 上运行交叉编译是 :arm-linux-gnueabihf-gcc udpc.c -o udpc
// Client side implementation of UDP client-server model
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8080
#define MAXLINE 1024
// Driver code
int main(int argc,char **argv) {
int sockfd;
char buffer[MAXLINE];
char *hello = "Hello from leon client";
struct sockaddr_in servaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
if(argc>=2)
{
inet_pton(AF_INET,argv[1],&(servaddr.sin_addr));
}
if(argc>=3){
int sport;
sscanf(argv[2],"%d",&sport);
servaddr.sin_port=htons(sport);
}
int n, len;
sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
printf("Hello message sent.\n");
char serverbuffer[20];
int sport;
n = recvfrom(sockfd, (char *)buffer, MAXLINE,
MSG_WAITALL, (struct sockaddr *) &servaddr,
&len);
buffer[n] = '\0';
printf("Server : %s\n", buffer);
inet_ntop(AF_INET,&(servaddr.sin_addr.s_addr),serverbuffer,len);
sport=ntohs(servaddr.sin_port);
printf("server=%s, port=%d len=%d\n",serverbuffer,sport,len);
close(sockfd);
return 0;
}
代码结构是:建立socket 文件号, 填充服务端信息,缺省是本机,端口号为8080。 但可以命令行输入更改,比如:
./udpc 192.168.1.100 8000
发送内容在hello,然后接收,内容在buffer, 显示内容,关闭连接,结束。
4:结果显示
这是在ubuntu16 上的显示
服务端先启动:等待客户来连接,有连接后显示送来的包,解析客户端地址和端口,回送,退出。
liwenz@ubuntu:~/tmp$ ./udps
before recvfrom len =16
Client : Hello from leon client
caddr=100007f
addr=127.0.0.1, len=16
c family=2
c port=52942 and=52942
Hello message sent.
check server addr=0.0.0.0, port=8080
liwenz@ubuntu:~/tmp$
客户端后启动,发送包后,接收服务端的回送包,然后显示,退出。
liwenz@ubuntu:~/tmp$ ./udpc
Hello message sent.
Server : Hello from server
server=127.0.0.1, port=8080 len=16
liwenz@ubuntu:~/tmp$
在petalinux 上的显示也是一样的。但最开始的程序,不能解析客户端的地址。
主要原因是,服务端的recvfrom 中的len 应该先有值。所以服务端开始显示 before recvfrom len=16。
最开始没有赋值,也没有显示校验。 在ubuntu 16 上运行正常,但petalinux 上不能解析地址和端口号。
更改后,就在petalinux 上正常了。