Unity与MFC之间通信(基于socket)

Posted NCHU香菇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity与MFC之间通信(基于socket)相关的知识,希望对你有一定的参考价值。

最近公司有需求要在mfc和unity进行消息互发和响应,我通过查找网上资料以及改写最后实现了这一功能。

这里对实现方法进行一下记录。

1、在Unity中添加服务器代码 ServerSocket.cs:

//ServerSocket.cs
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.IO;
using UnityEngine;

public class ServerSocket
{

    private static ServerSocket _instance;

    Socket severSocket;//服务器端Socket
    Socket clientSocket;//客户端
    Thread thread;//连接线程
    IPEndPoint clientip;//被连接的ip地址
    string returnStr;//用于传递消息的字符串
    string receiveStr;//接收客户端发来的字符串
    string sendStr;//发送的字符串

    int recv;//用于表示客户端发送的信息长度
    byte[] receiveData = new byte[1024];//用于缓存客户端所发送的信息,通过socket传递的信息必须为字节数组
    byte[] sendData = new byte[1024];//用于缓存客户端所发送的信息,通过socket传递的信息必须为字节数组

    public static ServerSocket Instance
    {
        get
        {
            if(_instance == null)
            {
                _instance = new ServerSocket();
                _instance.Init();
            }
            return _instance;
        }

        set
        {
            _instance = value;
        }
    }


    //程序初始化
    public void Init()
    {
        //初始化命令字符串
        returnStr = null;
        receiveStr = null;

        //获取ip
        string hostName = System.Net.Dns.GetHostName();
        System.Net.IPHostEntry ipEntry = System.Net.Dns.GetHostEntry(hostName);
        //ip地址列表
        System.Net.IPAddress[] addr = ipEntry.AddressList;

        //建立服务器端socket
        for (int i = 0; i < addr.Length; i++)
        {
            //从IP地址列表中筛选出IPv4类型的IP地址
            //AddressFamily.InterNetwork表示此IP为IPv4,
            //AddressFamily.InterNetworkV6表示此地址为IPv6类型
            if (addr[i].AddressFamily == AddressFamily.InterNetwork)
            {
                IPEndPoint ipep = new IPEndPoint(addr[i], 8000);//本机预使用的IP和端口
                Debug.Log(addr[i].ToString());
                severSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                severSocket.Bind(ipep);//绑定
                severSocket.Listen(10);//监听
                                       //建立服务器端socket end
                break;
            }
        }

        //新建线程
        thread = new Thread(new ThreadStart(GoClient));
        //启动线程
        thread.Start();
    }

    void GoClient()
    {
        //客户端连接
        ConnetClient();
        //用死循环来不断的从客户端获取信息
        while (true)
        {
            //每次接收数据之前先清空字符数组
            receiveData = new byte[1024];
            recv = clientSocket.Receive(receiveData);
            //当信息长度为0,说明客户端连接断开
            if (recv == 0)
            {
                //等待客户端重新连接
                ConnetClient();
                //进入下一次循环
                continue;
            }
            //接收到的消息
            receiveStr = Encoding.ASCII.GetString(receiveData, 0, recv);
        }
    }

    //等待客户端连接
    void ConnetClient()
    {
        if (clientSocket != null)
        {
            clientSocket.Close();
        }
        //等待连接
        //当有可用的客户端连接尝试时执行,并返回一个新的socket,用于与客户端之间的通信
        clientSocket = severSocket.Accept();
    }

    //向客户端发送信息
    public void SendClient(string str)
    {
        sendData = new byte[1024];
        sendData = Encoding.ASCII.GetBytes(str);
        clientSocket.Send(sendData, sendData.Length, SocketFlags.None);
    }

    //返回传送命令
    public string ReturnStr()
    {
        lock (this)
        {
            returnStr = receiveStr;
        }
        return returnStr;
    }
    //退出整个socket
    public void SocketQuit()
    {
        //先关闭客户端
        if (clientSocket != null)
        {
            clientSocket.Close();
        }
        //再关闭线程
        if (thread != null)
        {
            thread.Interrupt();
            thread.Abort();
        }
        //最后关闭服务端socket
        severSocket.Close();
    }
}

2、MFC中客户端实现

在对话框中新建两个变量:

SOCKET  m_socket;
 HWND apphWnd = NULL;

启动Unity并将unity嵌入mfc窗口:

if (apphWnd != NULL)
	{
		EndDialog(IDCANCEL);
	}

	CRect rect;
	//GetClientRect(&rect); // 如需全屏请替换为该代码
	CWnd *pView = GetDlgItem(IDC_VIEW); // 这个空间用来表示unity视口大小,获取size后该控件将会被隐藏
	pView->GetWindowRect(&rect);
	this->ScreenToClient(&rect);
	pView->ShowWindow(SW_HIDE);

	//获取应用程序目录
	CString strPath;
	WCHAR szPath[255];
	memset(szPath, 0, 255);
	GetModuleFileName(NULL, szPath, MAX_PATH);
	strPath = szPath;
	int nIndex = strPath.ReverseFind(L'\\\\');
	if (nIndex == -1)
	{
		return;
	}
	strPath = strPath.Left(nIndex + 1);
	strPath.Append(L"MyProject001.exe");

	hProcess = StartProcess(strPath, L"");//Start ms paint
	
	if (apphWnd != NULL)//check for window handle
	{

		//::SetParent(apphWnd, m_hWnd);//set parent of ms paint to our dialog.
		CWnd *pUnityWnd = CWnd::FromHandle(apphWnd);
		pUnityWnd->ModifyStyle(NULL, WS_CHILD);
		pUnityWnd->SetParent(this);
		SetWindowLong(apphWnd, GWL_STYLE, WS_VISIBLE);//eraze title of ms paint window.
													  //Positioning ms paint.
		::MoveWindow(apphWnd, rect.left, rect.top, rect.right, rect.bottom, true);
		//::SendMessage(apphWnd, WM_SIZE, 0, 0);
		//窗口重绘,(因创建exe时,设置为SW_HIDE,导致exe窗口会被父窗口覆盖一部分)
		Invalidate();
		::UpdateWindow(apphWnd);
		::ShowWindow(apphWnd, SW_SHOW);
	}
	else
		MessageBox(L"Cannot find Window");

初始化Socket:

    WSADATA wsaData;
	::WSAStartup(MAKEWORD(2, 2), &wsaData);
	//初始化套接字
	m_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == m_socket)
	{
		MessageBox(L"套接字创建失败!");
		return FALSE;
	}

连接服务器:

// 临时保存IP
	char cHostName[256] = { 0 };
	gethostname(cHostName, sizeof(cHostName));
	struct hostent *pHost = gethostbyname(cHostName);
	in_addr addr;
	memcpy(&addr, pHost->h_addr_list[0], sizeof(in_addr));

	SOCKADDR_IN addrTo;
	addrTo.sin_family = AF_INET;
	addrTo.sin_port = htons(8000);
	addrTo.sin_addr.S_un.S_addr = addr.s_addr;

	int iResult = connect(m_socket, (SOCKADDR*)&addrTo, sizeof(addrTo));

	if (iResult == SOCKET_ERROR)
	{
		MessageBox(L"连接服务器不成功!");
		//WSACleanup();
		//continue;
	}
	else
	{
		MessageBox(L"连接服务器成功!");
	}

3、消息响应

unity响应mfc消息:

        //接收消息并处理
        if (ServerSocket.Instance.ReturnStr() != null)
        {
            str = ServerSocket.Instance.ReturnStr();
            if ("mfc发过来的消息" == str)
            {
               // unity要进行的处理
            }
        }

unity向mfc发送消息:

ServerSocket.Instance.SendClient("unity要发送的消息");

mfc响应unity消息:(通过开启一个线程,死循环接收消息)

unsigned int threadID;
	HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, &MessageLoop, (LPVOID)this, 0, &threadID);
static unsigned __stdcall MessageLoop(void * pParam);

unsigned __stdcall MessageLoop(void * pParam)
{
	char szMsg[256];
	//发送,接收数据
	while (1) {
		int recv_len = recv(m_socket, szMsg, 100, 0);
		if (recv_len < 0) {
			continue;
		}
		szMsg[recv_len] = 0;
		if (strcmp(szMsg, "unity发过来的消息") == 0)
		{
            // mfc要进行的处理
		}
	}
}

mfc向unity发送消息:

std::string strMsg = "mfc要发送的消息";
send(m_socket, strMsg, strlen(strMsg), 0);

以上是关于Unity与MFC之间通信(基于socket)的主要内容,如果未能解决你的问题,请参考以下文章

unity3D中使用Socket进行数据通信

C++ socket编程 和 MFC socket编程 有啥区别??

C++ socket编程 和 MFC socket编程 有啥区别??

(转载)用vs2010开发基于VC++的MFC 串口通信一*****两台电脑同一个串口号之间的通信

游戏开发实战用Go语言写一个服务器,实现与Unity客户端通信(Golang | Unity | Socket | 通信 | 教程 | 附工程源码)

游戏开发实战用Go语言写一个服务器,实现与Unity客户端通信(Golang | Unity | Socket | 通信 | 教程 | 附工程源码)