实现vs和matlab本机TCPIP通信

Visual Studio 与 Matlab 进行TCP/IP 通信传送数据

因工作需要将VS中得到的数据传送到Matlab使用patch()函数画图,从网上看到了相应的方法。但是

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

如文中所说Matlab默认的接收缓冲区是 512字节,所以如果matlab那边不做处理,C++那边一次最多传送64个数字。不满足实验需要,我需要传送stl模型中各个三角面片的点坐标,一次至少需要传输上千个数字。所以在这个基础上对代码进行了一些更改。

Matlab Client端

Matlab端作为客户端,由于传过来的是一堆 ACSII 码 (即 recv),所以先将这些码转换成 十六进制(dec2hex)的,然后横向展开,每十六位进行一次分割,然后再转换回 double (hex2num)。

clear
clc
close all
warning off all
%% 设置连接参数,要连接的地址为127.0.0.1(即本地主机),端口号为5000,作为客户机连接。
Client=tcpip('127.0.0.1',5000,'NetworkRole','client');
Client.InputBufferSize = 200000; %设置Client的输入缓存大小,大小至少为size * 8
Client.BytesAvailable
%% 建立连接,建立完成后进行下一步,否则报错
fopen(Client);%与一个服务器建立连接,直到建立完成返回,否则报错。
sprintf('成功建立连接')
%% 发送字符串
sendtxt = 'hello hello';
fprintf(Client,sendtxt);
%% 接收字符串
Client.BytesAvailable
pause(1);
recv=fread(Client,18816,'char');    
%sizeA至少为所需传递的double类型的个数乘以8 [2352*8=18816]
recv1 = dec2hex(recv);
recv2 = [];
for i = 1:length(recv1)
    recv2 = [recv2,recv1(i,:)]; %小端模式
end
count = 0;
recv3 = []; %转化成double
while count < length(recv1)/8
    recv3 = [recv3,hex2num(recv2(count*16+1:count*16+16))];
    count = count + 1;
end
recv3;
%% 关闭客户端
fclose(Client);
%% 删除客户端
delete(Client);

类型转换

C++这边Socket提供的接口提供函数只可以支持 char * 类型的指针,所以想要传递double数据,首先需要进行一步类型转换,将 double 转化成 char 数组,Matlab接收到的是 浮点数的 ACSII 码值。
在这一步试了以下两种方法:

  • 利用C++提供的sprintf() 函数 ,将浮点数 传化成 char 数组,但是一次性传送多个数字的话,在传到 Matlab 之后,由于每个数的大小不一样,位数不一样,Matlab 不好分割,没办法处理。
  • 利用 union ,将double的八个字节塞给一个char数组,这样就可以保证每一次传送的位数是固定的 8位。union 内部的所有数据公用同一个地址,所以这一部分转换如下。
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<iostream>
#include<string>
using namespace std;

const int len_double = sizeof(double);

typedef union doubletochar_8
{
	double num_double;
	char num_char[len_double];
}doubletochar;

int main()
{
	doubletochar num;
	char send_arr[len_double];
	double x = 10;
	num.num_double = x;
	for (int i = 0; i <= len_double-1;++i) //小端模式:低字节在低位,高字节在高地址
	{
		send_arr[i] = num.num_char[i];
		printf("%02x\n", send_arr[i]);
	}
	return 0;
}

Visual Studio Sever端

接下来就是在C++这边写服务器端了,Socket编程的流程如下:

  1. 初始化
  2. 创建服务器的套接字 serviceSocket
  3. 绑定套接字,指定绑定的 IP地址 和 端口号 :将 socketAddr 与 serviceSocket 绑定
  4. 服务器监听
  5. 接受请求 :初始化一个接受的客户端socket—recvClientSocket
  6. 接收/发送数据 :recv//send 这两个函数都要操作 recvClientSocket。这两个函数返回值是成功发送的位数。socket中send和recv函数 一般改动的话也就是改这一部分,这篇讲得比较好。
  7. 关闭socket
  8. 终止

socket.h

#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS 	
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<string>
#include<cstring>
#include<WS2tcpip.h>
#include <WinSock2.h>						//一般情况下,这个头文件位于windows.h之前,避免发生某些错误
#include<Windows.h>
#pragma comment(lib, "ws2_32.lib") 			//显示加载 ws2_32.dll	ws2_32.dll就是最新socket版本啦
#include "allstruct.h"

const int len_double = sizeof(double);
typedef union doubletochar_8
{
	double num_double;
	char num_char[len_double];
}doubletochar;

void VS2Mat(std::vector<double> in);

socket.cpp

#include "socket.h"

//只能发送double类型的数据
void VS2Mat(std::vector<double> in)
{
	std::cout << "-----------服务器-----------" << std::endl;
	const int BUF_SIZE = 4096;		//接受缓冲区大小为4096字节
	//	1	初始化
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);	//make word,你把鼠标移到WSAStartup看看参数列表,是不是就是一个word啊


	//	2	创建服务器的套接字socket
	SOCKET serviceSocket;
	serviceSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);	//socket(协议族,socket数据传输方式,某个协议) AF_INET
															    //  AF_INET:IPv4,SOCK_STREAM:传输层使用TCP协议,IPPROTO_TCP:TCP协议
	if (SOCKET_ERROR == serviceSocket) {
		std::cout << "套接字闯创建失败!" << std::endl;
	}
	else {
		std::cout << "套接字创建成功!" << std::endl;
	}


	//	3	绑定套接字	指定绑定的IP地址和端口号
	sockaddr_in socketAddr;								//一个绑定地址:有IP地址,有端口号,有协议族
	socketAddr.sin_family = AF_INET;
	socketAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");		// IP地址即本机地址
	socketAddr.sin_port = htons(5000); // 端口号
	int bRes = bind(serviceSocket, (SOCKADDR*)&socketAddr, sizeof(SOCKADDR));	//绑定注意的一点就是记得强制类型转换
	if (SOCKET_ERROR == bRes) {
		std::cout << "绑定失败!" << std::endl;
	}
	else {
		std::cout << "绑定成功!" << std::endl;
	}

	//	4	服务器监听	
	int lLen = listen(serviceSocket, 5);	//监听的第二个参数就是:能存放多少个客户端请求,到并发编程的时候很有用哦
	if (SOCKET_ERROR == lLen) {
		std::cout << "监听失败!" << std::endl;
	}
	else {
		std::cout << "监听成功!" << std::endl;
	}


	//	5	接受请求
	sockaddr_in revClientAddr;
	SOCKET recvClientSocket = INVALID_SOCKET;	//初始化一个接受的客户端socket
	int _revSize = sizeof(sockaddr_in);
	recvClientSocket = accept(serviceSocket, (SOCKADDR*)&revClientAddr, &_revSize);		//accept(执行listen的监听socket,用来获取接受连接的远端socket地址,远端socket地址的长度)
																					    //服务器可通过读写该socket来与被接受链接对应的客户端通信										
	if (INVALID_SOCKET == recvClientSocket) {
		std::cout << "服务端接受请求失败!" << std::endl;
	}
	else {
		std::cout << "服务端接受请求成功!" << std::endl;
	}

	//	6	发送/接受 数据
	//接受数据
	char recvBuf[BUF_SIZE];
	int reLen = recv(recvClientSocket, recvBuf, strlen(recvBuf), 0);
	recvBuf[reLen] = '\0';
	if (SOCKET_ERROR == reLen) {
		std::cout << "服务端接收数据失败" << std::endl;
	}
	else {
		std::cout << "服务器接受到数据:    " << recvBuf << std::endl;
		//cout << typeid(recvBuf).name() << endl;
	}

	//发送数据
	doubletochar num;
	int count = 0;
	int insize = in.size();
	while (count < insize)
	{
		char send_arr[len_double];
		num.num_double = in.at(count);
		for (int i = len_double - 1; i >= 0; --i)//小端模式:低字节在低位,高字节在高地址
		{
			send_arr[len_double - 1 - i] = num.num_char[i];
		}
		int sLen = send(recvClientSocket, send_arr, sizeof(send_arr), 0);
		if (SOCKET_ERROR == sLen) {
			std::cout << "count is " << 1 + count << "服务端发送数据失败" << std::endl;
		}
		else
		{
			//cout << "服务端发送数据成功" << endl;
			std::cout << "count is " << 1 + count << " num is " << in.at(count) << std::endl;
		}
		count++;
	}
	//	7	关闭socket
	closesocket(recvClientSocket);
	closesocket(serviceSocket);

	//	8	终止
	WSACleanup();

	std::cout << "服务器停止" << std::endl;
	std::cin.get();
}

程序执行

1.先开启服务端,然后开启客户端。

2.注意IP和端口一致

step1

开启服务端

开启服务端
step2:

打开客户端
打开客户端
服务器传输数据
服务器传输数据
可以看到recv3为1X2352的行向量,发送的数据也总共为2352个,说明数据发送接受均成功


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