最近公司有需求要在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);
版权声明:本文为qq_42608732原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。