您的位置:首页 > 移动开发 > Unity3D

Unity C#作为客户端与C++服务器 传递结构体进行Socket通讯

2018-12-04 11:23 134 查看

最近接触到C#和C++进行SOCKET通讯的问题。

讲述一下我的情况:

 和我合作的是一个只懂C++的程序员  , 而我是一个经常使用C# 对C++知之甚少的人,  没办法  半路出家。

C++的服务器是他写的     C#的客户端是我写的。  说实话,这个问题困扰了我长达2周之久。  期间我有一种我是中国人而那个人却是个美国人的感觉。

 

下面贴一下思路和代码

C++这边我不是特别会写,在网上找了个简单的代码稍微改了改建立Socket服务器

 

 

 

[code]#include <thread>
#include "winsock2.h"
#include <iostream>

WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0)
{
return 0;
}

//创建套接字
slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}

//绑定IP和端口
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = INADDR_ANY;

if (_WINSOCK2API_::bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("bind error !");
}

//开始监听
if (listen(slisten, 5) == SOCKET_ERROR)
{
printf("listen error !");
return 0;
}

//循环接收数据
thread task01(SendData);

 

SendData函数的方法可以自己写 , 这里开了个线程不听的发送。 方法体主要写自己要发送的内容

[code]//发送数据
void SendData()
{
while (true)
{
printf("等待连接...\n");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET)
{
printf("accept error !");
continue;
}
printf("接受到一个连接");

//发送数据

send(sClient, (char*)(&cvData), sizeof(OpenCvData), 0);

}
}

到这里就需要说一下 C++和C# 之间的数据格式需要在网上查一下  我这里发送的是一段Double类型的结构体。如下:

[code]struct OpenCvData
{
double pos[32];
double LinePos[4];
}cvData;

好了 C++部分的服务器已经完成了。 

现在是C#部分的客户端接收了,这里不累述。直接贴代码。  讲一下流程

1.建立SOCKET客户端

2.创建和C++对应类型的结构体 并序列化和规定好长度

3.解析接收到的字节,转换成自己需要的结构体

 

第一步的代码如下,初始化的时候就把SOCKET建立好  并开启线程接收。

[code]public ClientSocket(RobotT handler)
{

ip = IPAddress.Parse("127.0.0.1");
port = 8888;
reciveMsg = handler;
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.Connect(ip, port);

Thread reciveMessageFromServer = new Thread(ReciveMessageFromServer);
reciveMessageFromServer.Start();
}

 

 

 

第二步代码如下,这里要重点讲一下,因为我写结构体的时候并没有初始化 ,所以在新建一个对象以后要对他进行初始化

[code] private void ReciveMessageFromServer()
{
while (true)
{

if (clientSocket == null)
{
Debug.Log("客户端的套接字没了");
continue;
}

Debug.Log("服务器向客户端发送消息成功");
int bufferCount = clientSocket.Receive(buffer); //阻塞型接收
// string msg = Encoding.UTF8.GetString(buffer, 0, bufferCount);

try
{
DS = new DataStruct();
DS.Pos = new double[32];
DS.LinePos = new double[4];
DS = (DataStruct)BytesToStruct(buffer, DS.GetType());
for (int i = 0; i < DS.Pos.Length; i++)
{
Debug.Log(DS.Pos[i]);
}
for (int i = 0; i < DS.LinePos.Length; i++)
{
Debug.Log(DS.LinePos[i]);
}

}
catch (Exception)
{
Debug.Log("数据异常");
continue;
}

}
}

 

第三步其实应该是第二步  ,但是为了好给大家呈现解析的代码  就放在这里写了。

下面是和C++对应的结构体,这里需要说明一下   

第一行是序列化这个结构体

第二行是结构体的大小

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 这个是设置序列化的数据的长度

[code][Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DataStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public double[] Pos;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public double[] LinePos;

}

以下是字节转换成结构体的代码,直接复制粘贴就好了 

[code]
public object BytesToStruct(byte[] bytes, Type type)
{
int size = Marshal.SizeOf(type);
IntPtr structPtr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, structPtr, size);
object obj = Marshal.PtrToStructure(structPtr, type);
Marshal.FreeHGlobal(structPtr);
return obj;
}

全部代码就是这些了,写的比较急可能不是特别详细  。有不明白的可以问我

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: