最近公司有需求要在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);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294051.html
標籤:其他
下一篇:pygame中文亂碼問題
