基于linux环境下C++与matlab的TCP/IP通讯

        记一次和iiwa相关的项目,需要用到C++与matlab进行数据的实时通讯。数据的传输暂时只需要机器人的关节角度及空间坐标姿态(长度为7的double型数组)

        由于大部分的功能实现在matlab端,c++端作为调用,故将Server端实现在Matlab,Client端实现在C++。

        代码如下:

客户端(C++)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#define MAXLINE 4096
#define IP  "127.0.0.1"

	double joint[6]={10.123456,11.123456,12.123456,13.123456,14.123456,15.123456};
	double joint1[7]={0};
	const int len_double = sizeof(double);

void *dtoc(void *s,char *m,size_t n)//double型转换成char型
{
	char *ss = s;

	for (int i = 0; i < n; i++)
		m[i]=ss[i];
	return m;
}

void btos(char *s)//小端模式:低字节在低位,高字节在高地址
{
	char send_arr[len_double];
	for(int j=0;j<7;j++){
		for(int i=0;i<len_double;i++){
			send_arr[i]=s[i+j*len_double];
		}	
		for(int i=0;i<len_double;i++){
			s[7-i+j*len_double]=send_arr[i];
		}
	}
}



void *ctod(void *s,double *m,size_t n)//char型转换成double型
{
	double *ss = s;

	for (int i = 0; i < 7; i++)
		m[i]=ss[i];
	return m;
}

int main(){
    int   sockfd, n;
    char  recvline[4096], sendline[4096];
    struct sockaddr_in  servaddr;

    //建立一个socket
    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
        return 0;
    }

    //初始化  将串口号等信息进行绑定
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons(6668);


    //主动连接服务器 
    if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
        printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }

    printf("连接成功\n");


     int num=0;
//     while(1){

         printf("send msg to server: \n");

         dtoc(joint,sendline,sizeof(joint));

	     btos(sendline);

	//printf("%s\n",sendline);

         send(sockfd, sendline, strlen(sendline), 0);

             //strlen:用字符数组数量的末地址减去首地址计算字符长度
        // if( send(sockfd, sendline, strlen(sendline), 0) < 0){
        //    printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
        //      return 0;
        //  } 
          recv(sockfd, recvline, MAXLINE, 0);
	btos(recvline);
	ctod(recvline,joint1,sizeof(recvline));
	printf("%d\n",num++);
 	for(int i=0;i<7;i++){
         	printf(" %f ", joint1[i]);
        };
	printf("\n");
//     }


    close(sockfd);
    return 0;
}

        对上述代码进行解释:该代码主要分为两个部分。1、与来自Matlab的服务端进行通讯。2、将目标数组转换成方便传输读取的形式。

        1、与来自Matlab的服务端进行通讯。采用传统的TCP(socket通讯)

服务器端先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。

socket() -> bind() -> listen() -> accept()

客户端先初始化socket,然后与服务端连接,服务端监听成功则连接建立完成

socket() -> connect()

        2、将目标数组转换成方便传输读取的形式。

传输数据的格式选为char型(c++传输据说只能用char型,待有时间了验证一下,理论上应该都行才对),小端模式(低字节在低位,高字节在高位)

转换的部分有两点:1、将双精度数组转成char型,将char型转为双精度型;2、将数据格式转换为小端格式。分别为dtoc和ctod函数,及btos函数。

服务端(Matlab)

t_server=tcpip('127.0.0.1',6668,'NetworkRole','server');%与第一个请求连接的客户机建立连接,端口号为6668,类型为服务器。
t_server.InputBuffersize=10000;%缓冲区放大到10000
fopen(t_server);%打开服务器,直到建立一个TCP连接才返回;
sprintf("成功建立连接");
pause(1);

while(1)
    
    while(1)%等到缓存区有数据就跳出循环
     if  t_server.BytesAvailable>0
         %t_server.BytesAvailable%显示缓存区字节数
        break;
     end
    end
data_recv=(fread(t_server,t_server.BytesAvailable));%从缓冲区读取数字数据


%%%
data_send=[40.123456,21.123456,22.123456,23.123456,24.123456,25.123456,1];
send1=num2hex(data_send);%转成16进制

send2=[];
for j = 1:length(data_send)
    send2 = [send2,send1(j,:)]; %小端模式
end

count1 = 0;
send3 = []; %转化成double
while count1 < length(data_send)*8
    send3 = [send3,hex2dec(send2((count1*2+1):(count1*2+2)))];
    count1 = count1 + 1;
end

fwrite(t_server,send3,'char');

end

fclose(t_server);
%%%%将传入的char数组转成double型%%%%
recv1 = dec2hex(data_recv);%ACSII码转换成16进制

recv2 = [];                      %将数据按小端形式放在一起
for i = 1:length(recv1)
    recv2 = [recv2,recv1(i,:)]; %小端模式
end

%转化成double
count = 0;
recv3 = []; 
while count < length(recv1)/8
    recv3 = [recv3,hex2num(recv2((count*16+1):(count*16+16)))];
    count = count + 1;
end

%%%将double型转成char数组%%%%%%
send1=num2hex(data_send);%转成16进制

send2=[];
for j = 1:length(data_send)
    send2 = [send2,send1(j,:)]; %小端模式
end

%转化成double
count1 = 0;
send3 = []; 
while count1 < length(data_send)*8
    send3 = [send3,hex2dec(send2((count1*2+1):(count1*2+2)))];
    count1 = count1 + 1;
end

        Matlab部分除去TCP连接外,主要是关于将C++传入的char数组转换为double型,及将double型转换成char型再传给C++,转换原理为将10进制数转为16进制后,以小端模式放在一起,然后按所需的数据类型进行分割,在转回10进制数。

本文参考:Visual Studio C++与 Matlab 进行 TCP/IP 通信传送 double 类型数据

                        Linux C/C++ TCP Socket通信实例 等;

新人文章,如有错漏,欢迎大佬指出。


版权声明:本文为rushlalala原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。