Unity中c#为客户端,c++为服务器端进行socket通信
1.需求
最近在项目中,要求将unity中虚拟相机的坐标变化等信息用socket传入c++写的处理程序中。相当于用C#写客户端,c++写服务器端。主要参考了一下博客:http://blog.csdn.net/qq_34204419/article/details/82529386
2.注意C#与C++数据类型的对应关系
数据类型不对应,编码方式不一致都可能导致传输结果为乱码。
大佬分别写了一个C#类和c++结构体进行对齐。
c#:
[Serializable] //序列化对象
[StructLayout(LayoutKind.Sequential, Pack = 1)] // 按1字节对齐
public class UserMsg
{
public int messageID;
public int clientID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)] //限制200字节
public byte[] message;
}
c++:
typedef struct
{
int messageID;
int clientID;
char message[200];
}UserMsg;
3.code
我对大佬代码进行了一点改变,符合我的需求。
Unity,c#,客户端
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System;
using System.Runtime.InteropServices;
[Serializable]
[StructLayout(LayoutKind.Sequential,Pack=1)]
public class UserMsg
{
//public int messageID;
//public int clientID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] //限制50字节
public byte[] message;
}
public class Sockets : MonoBehaviour {
Socket socketsend;
public GameObject cube=null;
// List<byte> list = new List<byte>();
// Use this for initialization\
public static byte[] StructToBytes(object obj)
{
//得到结构体的大小
int size = Marshal.SizeOf(obj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(obj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, 0, size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
}
void Start ()
{
try
{
socketsend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint point = new IPEndPoint(ip, 50000);
socketsend.Connect(point);
Debug.Log("连接成功!");
//开启线程接收消息
Thread th = new Thread(Recieve);
th.IsBackground = true;
th.Start();
}
catch
{
Debug.Log("初始化错误");
}
}
private void OnGUI()
{
if (GUILayout.Button("结束"))
{
socketsend.Close();
}
}
// Update is called once per frame
void Update ()
{
try
{
if (socketsend.Connected)
{
string strCube_x = cube.transform.position.x.ToString();
string strCube_y = cube.transform.position.y.ToString();
string strCube_z = cube.transform.position.z.ToString();
Debug.Log(strCube_x);
string str_Position = strCube_x + "a" + strCube_y + "b" + strCube_z;
//Debug.Log("12"+str_Position);
UserMsg ux = new UserMsg();
ux.message = Encoding.ASCII.GetBytes(str_Position);
byte[] message_x = StructToBytes(ux);
socketsend.Send(message_x);
}
else
{
Debug.Log("发送结束!");
}
}
catch(SocketException se)
{
Debug.Log("发送失败!");
}
}
//
void Recieve()
{
while (true)
{
try
{
byte[] buffer = new byte[1024 * 1024 * 3];
int r = socketsend.Receive(buffer);
if (r == 0)
{
break;
Environment.Exit(0);//结束当前进程
}
string str = Encoding.UTF8.GetString(buffer, 0, r);
Debug.Log("receive:" + str);
}
catch { }
}
}
}
c++,服务器端
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <thread>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
//全局常量
const int BUF_SIZE = 50;
int flag = 1;
int flag1 = 1;
char inputC = 0;
char str1[] = "a";
char str2[] = "b";
//全局变量
SOCKET sockSer, sockCli;
SOCKADDR_IN addrSer, addrCli;
int naddr = sizeof(SOCKADDR_IN);
int retValx;
double cube_x ;
double cube_y ;
double cube_z ;
int w ;
int e ;
int tx ,ty;
char sendbuf[BUF_SIZE];
char inputbuf[BUF_SIZE];
//函数声明
typedef struct
{
char message[50];
}UserMsg;
void posReceive()
{
while (true) {
//ZeroMemory(recebuf, BUF_SIZE);
char recebuf_x[BUF_SIZE];
UserMsg u_x;
retValx= recv(sockCli, recebuf_x, BUF_SIZE, 0);
// cout << retValx << endl;
if (retValx<=0)
{
cout << " recv failed" << endl;
break;
}
u_x = *(UserMsg*)recebuf_x;
w = 0;
e = 0;
tx= 0;
ty= 0;
for (int i = 0; i < retValx; i++)
{
if (u_x.message[i] == str1[0] )
{
char message_x[50];
tx = i;
for (int j = 0; j < tx; j++)
{
message_x[j] = u_x.message[j];
}
cube_x = strtod(message_x, NULL);
}
if (u_x.message[i] == str2[0] )
{
ty = i;
char message_y[50];
char message_z[50];
for (int j = tx+1; j < ty; j++)
{
message_y[w] = u_x.message[j];
w++;
}
cube_y = strtod(message_y, NULL);
for (int j = ty+1; j < retValx; j++)
{
message_z[e] = u_x.message[j];
e++;
}
cube_z = strtod(message_z, NULL);
break;
}
}
cout << cube_x << ","<<cube_y<<","<<cube_z<<endl;
}
}
int main()
{
WSADATA wsdata;
if (WSAStartup(MAKEWORD(2, 2), &wsdata) != 0)
{
//输出出错信息
cout << "载入socket库出错!" << endl;
system("pause");
}
//创建Socket
sockSer = socket(AF_INET, SOCK_STREAM, 0);
//初始化地址
addrSer.sin_port = htons(50000);
addrSer.sin_family = AF_INET;
addrSer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//绑定Socket
bind(sockSer, (SOCKADDR*)&addrSer, sizeof(SOCKADDR));
cout << "bind success!" << endl;
while (flag)
{
//监听
listen(sockSer, 2);
//接受连接请求
sockCli=accept(sockSer,(SOCKADDR*)&addrCli,&naddr);
if (sockCli != INVALID_SOCKET) {
cout << "连接成功!" << endl;
strcpy_s(sendbuf, "hello!");
send(sockCli,sendbuf,sizeof(sendbuf),0);
thread t1(posReceive);
t1.join();
flag = 0;
}
}
while (flag1)
{
if (inputC == 27)
{
closesocket(sockSer);
closesocket(sockCli);
WSACleanup();
flag1 = 0;
}
}
return 0;
}
4. 主要思想
将相机的每一帧的位置进行发送:
string strCube_x = cube.transform.position.x.ToString();
string strCube_y = cube.transform.position.y.ToString();
string strCube_z = cube.transform.position.z.ToString();
主要为x,y,z的坐标。将他们变为一个str类型进行发送。因为主要为数字,在xyzz坐标中加入字母进行区分。在服务端在进行遍历,分解。
5.结果
Unity
c++端
欢迎大佬批评指正。