Unity中c#為使用者端,c++為伺服器端進行socket通訊

2020-09-27 09:00:52

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++端

在這裡插入圖片描述在這裡插入圖片描述歡迎大佬批評指正。