一次成功流水账-双机UDP通讯代码模板

使用环境为ubuntu16.04,如果出现动态库.so的依赖问题,将相应的文件放到依赖的目录即可。

比如复制

sudo cp  ./ld-linux-aarch64.so.1  /lib/ld-linux-aarch64.so.1

一、单机测试

服务器端代码,用来监听客户端发送的消息

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <typeinfo> 

using namespace std;
//创建新的套接字之前需要调用一个引入Ws2_32.dll库的函数,否则服务器和客户端连接不上
#pragma comment(lib,"ws2_32.lib")

struct TEST
{
	int test;
};
struct INFO
{
	int year;
	int month;
	int day;
	TEST test_struct;
	double temperature;
};

int main(int argc, char* argv[])
{	
	struct sockaddr_in Server;				//创建服务端sockaddr_in结构体
	//建立一个数据报类型的UDP套接字  ******************//
	int serverSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
	//设置服务器地址addrSrv和监听端口
	Server.sin_family = AF_INET;
	Server.sin_addr.s_addr = inet_addr("10.0.0.3"); //设置服务器主机ip地址(与接收方客户端的IP对应)
	Server.sin_port = htons(8001);					 //发送用的端口,可以根据需要更改
	//使用bind()函数绑定监听端口,将socket文件描述符sockSrv与地址类型变量(struct sockaddr_in )进行绑定
	//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	bind(serverSocket, (sockaddr *)&Server, sizeof(sockaddr));

	//*******************************************************************************************//
	struct sockaddr_in Client;				//创建客户端sockaddr_in结构体
	int clientSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
	Client.sin_family = AF_INET;
	Client.sin_addr.s_addr = inet_addr("127.0.0.1");
	Client.sin_port = htons(8001);

//*******************************************************************************************************
	//创建发送和接受的结构体以及转化时用到的字符串,用作测试
	INFO sendinfo,recvinfo;
	char send_info_buf[100], recv_info_buf[100];
	//创建发送和接受的字符串,用作测试
	char sendbuf[100]= "Hello!", recvbuf[100];
	//对发送的结构体成员赋值
	sendinfo.year = 1999;
	sendinfo.month = 11;
	sendinfo.day = 20;
	sendinfo.test_struct.test = 10;
	sendinfo.temperature = 18.30;
	//将发送的结构体sendinfo转化为字符串send_info_buf
	memcpy(send_info_buf, &sendinfo, sizeof(sendinfo));
//********************************************************************************************************

	while (1)
	{	
		
		//发送
			/*  
				int sendto(int s, const void* buf, int len, unsigned int flags, const struct sockaddr* to, int tolen);
				s:     接收端的socket描述符
				buf:  UDP数据报缓存区(包含待发送数据)
				len:  UDP数据报长度
				flags:调用方式标志位(一般设置为0)
				to:   指向接收数据的主机地址信息的结构体,(sockaddr_in需类型转换);
				tolen:同所指的结构体的长度
			*/	
			//字符串发送和打印所用语句
			//int t = sendto(clientSocket, sendbuf, sizeof(sendbuf), 0, (sockaddr*)&Client, sizeof(Client));	
			//cout << "sendto_len:  "<<t << endl;//若发送失败。则返回-1	
			//结构体发送和打印所用语句
			//int t = sendto(clientSocket, send_info_buf, sizeof(send_info_buf)+1  , 0, (sockaddr*)&Client, sizeof(Client));
			//cout << "sendto_len:  "<<t << endl<<endl;//若发送失败。则返回-1

		//接收
			/*  
				int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);
				s:       socket描述符
				buf:    UDP数据报缓存区(包含所要接受数据)
				len:    缓存区长度
				flags:  调用方式标志位(一般设置为0)
				from:   指向发送数据的客户端地址信息的结构体,(sockaddr_in需类型转换);
				fromlen:指针,指向from中结构体的长度
			*/
			socklen_t len = sizeof(sockaddr);
			//字符串接收和打印所用语句
			//recvfrom(serverSocket, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&Server, &len);
			//printf("%s\n\n", recvbuf);

			//结构体接收和打印所用语句
			recvfrom(serverSocket, recv_info_buf, sizeof(recv_info_buf), 0, (sockaddr *)&Server, &len);
			memcpy(&recvinfo, recv_info_buf, sizeof(recv_info_buf));
			cout << "Today is  " << recvinfo.year << "." << recvinfo.month<<"." << recvinfo.day << endl;
			cout << "The template is " << recvinfo.temperature << endl;
			cout << "The struct-test number is " << recvinfo.test_struct.test << endl<<endl;
	}
	// close(clientSocket);
	close(serverSocket);
	return 0;
}

客户端代码,用来发送的消息

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <typeinfo> 

using namespace std;
//创建新的套接字之前需要调用一个引入Ws2_32.dll库的函数,否则服务器和客户端连接不上
#pragma comment(lib,"ws2_32.lib")

struct TEST
{
	int test;
};
struct INFO
{
	int year;
	int month;
	int day;
	TEST test_struct;
	double temperature;
};

int main(int argc, char* argv[])
{
	struct sockaddr_in Server;				//创建服务端sockaddr_in结构体
	//建立一个数据报类型的UDP套接字  ******************//
	int serverSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
	//设置服务器地址addrSrv和监听端口
	Server.sin_family = AF_INET;
	Server.sin_addr.s_addr = inet_addr("127.0.0.1"); //设置服务器主机ip地址(与接收方客户端的IP对应)
	Server.sin_port = htons(8001);					 //发送用的端口,可以根据需要更改


	//使用bind()函数绑定监听端口,将socket文件描述符sockSrv与地址类型变量(struct sockaddr_in )进行绑定
	//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	bind(serverSocket, (sockaddr *)&Server, sizeof(sockaddr));

	//*************************************************************************************************************//
	struct sockaddr_in Client;				//创建客户端sockaddr_in结构体
	int clientSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
	Client.sin_family = AF_INET;
	Client.sin_addr.s_addr = inet_addr("10.0.0.3");
	Client.sin_port = htons(8001);


//*************************************************************************************************************//
	
	//创建发送和接受的结构体以及转化时用到的字符串,用作测试
	INFO sendinfo,recvinfo;
	char send_info_buf[100], recv_info_buf[100];
	//创建发送和接受的字符串,用作测试
	char sendbuf[100]= "How are you?", recvbuf[100];
	//对发送的结构体成员赋值
	sendinfo.year = 2021;
	sendinfo.month = 7;
	sendinfo.day = 27;
	sendinfo.test_struct.test = 10;
	sendinfo.temperature = 23.50;
	//将发送的结构体sendinfo转化为字符串send_info_buf
	memcpy(send_info_buf, &sendinfo, sizeof(sendinfo));
//************************************************************************************************************//

	while (1)
	{	
		usleep(2000000);
		//发送
			/*  
				int sendto(int s, const void* buf, int len, unsigned int flags, const struct sockaddr* to, int tolen);
				s:       Code1     接收端的socket描述符
				buf:    UDP数据报缓存区(包含待发送数据)
				len:     UDP数据报长度
				flags: 调用方式标志位(一般设置为0)
				to:      指向接收数据的主机地址信息的结构体,(sockaddr_in需类型转换);
				tolen:所指的结构体的长度
			*/	
			//字符串发送和打印所用语句
			//int t = sendto(clientSocket, sendbuf, sizeof(sendbuf), 0, (sockaddr*)&Client, sizeof(Client));	
			//cout << "sendto_len:  "<<t << endl<< endl;//若发送失败。则返回-1	
			//结构体发送和打印所用语句
			int t = sendto(clientSocket, send_info_buf, sizeof(send_info_buf)+1  , 0, (sockaddr*)&Client, sizeof(Client));
			cout << "sendto_len:  "<<t << endl<< endl;//若发送失败。则返回-1

		//接收
			/*  
				int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);
				s:       socket描述符
				buf:    UDP数据报缓存区(包含所要接受数据)
				len:    缓存区长度
				flags:  调用方式标志位(一般设置为0)
				from:   指向发送数据的客户端地址信息的结构体,(sockaddr_in需类型转换);
				fromlen:指针,指向from中结构体的长度
			*/

			//字符串接收和打印所用语句
			//socklen_t len = sizeof(sockaddr);
			//recvfrom(serverSocket, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&Server, &len);
			//printf("%s\n\n", recvBuf);

			//结构体接收和打印所用语句
			//recvfrom(serverSocket, recv_info_buf, sizeof(recv_info_buf), 0, (sockaddr *)&Server, &len);
			//memcpy(&recvinfo, recv_info_buf, sizeof(recv_info_buf));
			//cout << "Today is  " << recvinfo.year << "." << recvinfo.month<<"." << recvinfo.day << endl;
			//cout << "The template is " << recvinfo.temperature << endl;
			//cout << "The struct-test number is " << recvinfo.test_struct.test << endl<< endl;
			
	}
	close(clientSocket);
	// close(serverSocket);
	return 0;
}

进行编译

g++ client.cpp -o client -pthread
g++ server.cpp -o server -pthread

两个terminal分别运行,server端收到相应的信息。

./client
./server

两个terminal分别运行,server端收到相应的信息。将主机与机器人用网线链接,并设置主机ip与机器人为同一网段。主机端执行./server即可发现能够收到相应的信息。服务器端代码,用来监听客户端发送的消息。ssh登录进去后,执行./client。发送客户端可执行程序到机器人。客户端代码,用来发送的消息。

二、双机测试

将主机与机器人用网线链接,并设置主机ip与机器人为同一网段

发送客户端可执行程序到机器人

scp ./client  user@10.0.0.34:/home/user

ssh登录进去后,执行./client

主机端执行./server即可发现能够收到相应的信息。

三、Python版本

主机代码server.py,更加简洁,不需要编译。主机端在不停的发送字符串

#!/usr/bin/env python
import socket
import time
address=('192.168.31.20',10000)
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.bind(address)


data,addr=s.recvfrom(2048)
print "got data from",addr
#if (i%50) <= 25:
sdata=" 0.2  0.0" 	
#else:
	#sdata="-0.2  0.0"

for i in range(1000):
	time.sleep(1/10.0)
	if (i%50) <= 25:
		sdata="-0.2  0.0" 	
	else:
		sdata="-0.2  0.0"
	s.sendto(sdata,addr)
s.close()

客户端代码client.py,此处两段代码并不对应

#!/usr/bin/env python
import socket
addr=('192.168.31.247',10000)
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while 1:
	data=raw_input()
	if not data:
		break
	s.sendto(data,addr)
s.close()

实际使用laikago的sdk,客户端为C++,其数据解析及实现代码如下:

/************************************************************************
Copyright (c) 2018-2019, Unitree Robotics.Co.Ltd. All rights reserved.
Use of this source code is governed by the MPL-2.0 license, see LICENSE.
************************************************************************/

#include "laikago_sdk/laikago_sdk.hpp"
#include <math.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>


#define DEST_PORT 10000   //端口号
#define DSET_IP_ADDRESS  "192.168.31.20 " // server文件所在PC的IP

using namespace laikago;

// 16B
char send_buf[20] = "ready";

int send_num;
int recv_num;char recv_buf[20];
int sock_fd;
struct sockaddr_in addr_serv;
int len;
float f = 0.0, v = 0.0;

typedef union {
    struct {
        uint8_t R1          :1;
        uint8_t L1          :1;
        uint8_t start       :1;
        uint8_t select      :1;
        uint8_t R2          :1;
        uint8_t L2          :1;
        uint8_t F1          :1;
        uint8_t F2          :1;
        uint8_t A           :1;
        uint8_t B           :1;
        uint8_t X           :1;
        uint8_t Y           :1;
        uint8_t up          :1;
        uint8_t right       :1;
        uint8_t down        :1;
        uint8_t left        :1;
    } components;
    uint16_t value;
} xKeySwitchUnion;

typedef union {
    struct {
        uint8_t useAppBtJoy          :1;  // use bluetooth joy or not
        // uint8_t L1          :1;
        // uint8_t start       :1;
        // uint8_t select      :1;
        // uint8_t R2          :1;
        // uint8_t L2          :1;
        // uint8_t F1          :1;
        // uint8_t F2          :1;
    } components;
    uint8_t value;
} xAPPSwitchUnion;

// 40 Byte (now used 24B)
typedef struct {
    uint8_t head[2];
    xKeySwitchUnion btn;
    float lx;
    float rx;
    float ry;
    float L2;
    float ly;

    // xKeySwitchUnion app_btn; //this way is better
    uint8_t useAppBtJoy;

} xRockerBtnDataStruct;



class CustomData
{
public:
    CustomData(): udp(HIGH_CMD_LENGTH, HIGH_STATE_LENGTH){
        this->motiontime = 0;
        this->cmd = {0};
        this->state = {0};
    }
    
    UDP udp;
    HighCmd cmd;
    HighState state;
    int motiontime;

    xRockerBtnDataStruct         joy_wifi2Pc;
};


void UDPRecv(void *param)
{
    UDP *data = (UDP *)param; 
    data->Recv();
}

double toDouble(const char* s, int start, int stop) {
    unsigned long long int m = 1;
    double ret = 0;
    for (int i = stop; i >= start; i--) {
        ret += (s[i] - '0') * m;
        m *= 10;
    }
    return ret;
}

void RobotControl(void *param) 
{
    char *subString;
    char *subStringNext;
    float linvel;
    float rotvel;


    recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len);

    if(recv_num < 0)
    {
        perror("recvfrom error:");
        exit(1);
    }

    recv_buf[recv_num] = '\0';
    printf("client receive %d bytes: %s\n", recv_num, recv_buf);
    recv_buf[4]='\0';
    linvel = atof(recv_buf);
    rotvel = atof(&recv_buf[5]);

    printf("linevel: %f\n", linvel);
    printf("rotvel: %f\n", rotvel);



    CustomData *data = (CustomData *)param; 

    data->motiontime += 2;
    data->udp.GetRecv(data->state);
    // printf("%f\n", state.forwardSpeed);

    data->cmd.forwardSpeed = 0.0f;
    data->cmd.sideSpeed = 0.0f;
    data->cmd.rotateSpeed = 0.0f;
    data->cmd.bodyHeight = 0.0f;

    data->cmd.mode = 0;
    data->cmd.roll  = 0;
    data->cmd.pitch = 0;
    data->cmd.yaw = 0;

    // data->joy_wifi2Pc.useAppBtJoy = true;
    // memcpy(data->cmd.AppBtCmd, &data->joy_wifi2Pc, 40);
    data->cmd.mode = 2;
    data->cmd.forwardSpeed = linvel; // -1  ~ +1
    //printf("linevel: %f\n", data->cmd.forwardSpeed);
    //data->cmd.rotateSpeed = rotvel;   // turn
    

    
    // if(data->motiontime>20000 ){
    //     data->cmd.mode = 1;
    //     data->joy_wifi2Pc.useAppBtJoy = false;
    //     memcpy(data->cmd.AppBtCmd, &data->joy_wifi2Pc, 40);
    // }

    data->udp.Send(data->cmd);
}

int main(void) 
{
    // added  by Yuan Gao
    /************************************************/


    /* 建立udp socket */
    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock_fd < 0)
    {
        perror("socket");
        exit(1);
    }

    /* 设置address */
    // struct sockaddr_in addr_serv;
    // int len;
    memset(&addr_serv, 0, sizeof(addr_serv));
    addr_serv.sin_family = AF_INET;
    addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);
    addr_serv.sin_port = htons(DEST_PORT);
    len = sizeof(addr_serv);


    // int send_num;
    // int recv_num;
    // char send_buf[20] = "ready";
    // char recv_buf[20];

    // printf("client send: %s\n", send_buf);
    //   send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len);

    // if(send_num < 0)
    // {
    //     perror("sendto error:");
    //     exit(1);
    // }

    // recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len);

    // if(recv_num < 0)
    // {
    //     perror("recvfrom error:");
    //     exit(1);
    // }

    // recv_buf[recv_num] = '\0';
    // printf("client receive %d bytes: %s\n", recv_num, recv_buf);

    // close(sock_fd);
    /************************************************/
    // added by Yuan Gao

    printf("client send: %s\n", send_buf);
    send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len);

    if(send_num < 0)
    {
        perror("sendto error:");
        exit(1);
    }

    std::cout << "Control level is set to HIGH-level." << std::endl
              << "WARNING: Make sure the robot is standing on the ground." << std::endl
              << "Press Enter to continue..." << std::endl;
    std::cin.ignore();

    Control control(HIGHLEVEL);
    CustomData custom;
    control.InitCmdData(custom.cmd);
    control.loop.RegistFunc("UDP/Send", RobotControl, &custom);
    control.loop.RegistFunc("UDP/Recv", UDPRecv, &custom.udp);
    control.loop.Start();
    close(sock_fd);
    return 0; 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值