最近接触到C#和C++进行SOCKET通讯的问题。
讲述一下我的情况:
和我合作的是一个只懂C++的程序员 , 而我是一个经常使用C# 对C++知之甚少的人, 没办法 半路出家。
C++的服务器是他写的 C#的客户端是我写的。 说实话,这个问题困扰了我长达2周之久。 期间我有一种我是中国人而那个人却是个美国人的感觉。
下面贴一下思路和代码
C++这边我不是特别会写,在网上找了个简单的代码稍微改了改建立Socket服务器
#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函数的方法可以自己写 , 这里开了个线程不听的发送。 方法体主要写自己要发送的内容
//发送数据
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类型的结构体。如下:
struct OpenCvData
{
double pos[32];
double LinePos[4];
}cvData;
好了 C++部分的服务器已经完成了。
现在是C#部分的客户端接收了,这里不累述。直接贴代码。 讲一下流程
1.建立SOCKET客户端
2.创建和C++对应类型的结构体 并序列化和规定好长度
3.解析接收到的字节,转换成自己需要的结构体
第一步的代码如下,初始化的时候就把SOCKET建立好 并开启线程接收。
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();
}
第二步代码如下,这里要重点讲一下,因为我写结构体的时候并没有初始化 ,所以在新建一个对象以后要对他进行初始化
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)] 这个是设置序列化的数据的长度
[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;
}
以下是字节转换成结构体的代码,直接复制粘贴就好了
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;
}
全部代码就是这些了,写的比较急可能不是特别详细 。有不明白的可以问我