unity開發:Qt C++與unity之間TCP網路通訊
阿新 • • 發佈:2019-01-29
考慮實現用C++做伺服器,unity做客戶端實現TCP網路通訊。
以下采用TCP單執行緒連線。
Qt C++服務端
建立一個Qt的GUI專案,在介面上放一個label顯示連線狀態,兩個button作為指令傳送控制。
記得在pro檔案中加入network模組
widget.h
widget.cpp#ifndef WIDGET_H #define WIDGET_H #include <QWidget> class QTcpServer;//前向宣告 class QTcpSocket; namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private: Ui::Widget *ui; private: QString statusText; //狀態資訊 QTcpServer *tcpServer; //伺服器 QTcpSocket *clientTcpSocket; //客戶端socket void SocketSend(QString sendStr); private slots: void SocketConnet(); void SocketReceive(); void on_leftBtn_clicked(); void on_rightBtn_clicked(); }; #endif // WIDGET_H
main.cpp未更改就不貼了。#include <QTcpServer> #include <QTcpSocket> #include <QAbstractSocket> #include <QDebug> #include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //初始化server並監聽 tcpServer=new QTcpServer(this); //qt自己記憶體管理 if(!tcpServer->listen(QHostAddress::Any,6666)) //監聽所有網路地址,埠6666 qDebug()<<tcpServer->errorString(); statusText=statusText+"wait for connecting..."+"\n"; ui->statusLabel->setText(statusText); //繫結訊號槽,當有連線時作出反應 connect(tcpServer,SIGNAL(newConnection()),this,SLOT(SocketConnet())); } void Widget::SocketConnet() { //獲得client socket clientTcpSocket=tcpServer->nextPendingConnection(); //繫結訊號槽,接收資料,並且當連線關閉是刪除連線 connect(clientTcpSocket,SIGNAL(readyRead()),this,SLOT(SocketReceive())); connect(clientTcpSocket,SIGNAL(disconnected()),clientTcpSocket,SLOT(deleteLater())); //顯示客戶端連線資訊 QString clientIp=clientTcpSocket->peerAddress().toString(); QString clientPort=QString::number(clientTcpSocket->peerPort()); statusText=statusText+"conneted with "+clientIp+":"+clientPort+"\n"; ui->statusLabel->setText(statusText); } void Widget::SocketSend(QString sendStr) { clientTcpSocket->write(sendStr.toStdString().c_str()); } void Widget::SocketReceive() { //接收資料並顯示,位元組轉換成了字串 QString recvStr=clientTcpSocket->readAll(); statusText=statusText+recvStr+"\n"; ui->statusLabel->setText(statusText); //經處理後傳送回去 SocketSend("From server: "+recvStr); } Widget::~Widget() { delete ui; } //傳送unity物體左旋訊息 void Widget::on_leftBtn_clicked() { SocketSend("leftrotate"); } //傳送unity物體右旋訊息 void Widget::on_rightBtn_clicked() { SocketSend("rightrotate"); }
unity C#客戶端
建立一個unity場景,拖入一個cube
把tcpsocket連線部分封裝成了一個單獨的類TcpClientHandler,再加一個指令碼TcpTest掛到場景中,在這個指令碼中例項化用於連線的TcpClientHandler。
TcpClientHandler.cs
TcpTest.csusing UnityEngine; using System.Collections; //引入庫 using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; public class TcpClientHandler:MonoBehaviour { Socket serverSocket; //伺服器端socket IPAddress ip; //主機ip IPEndPoint ipEnd; string recvStr; //接收的字串 string sendStr; //傳送的字串 byte[] recvData=new byte[1024]; //接收的資料,必須為位元組 byte[] sendData=new byte[1024]; //傳送的資料,必須為位元組 int recvLen; //接收的資料長度 Thread connectThread; //連線執行緒 //初始化 public void InitSocket() { //定義伺服器的IP和埠,埠與伺服器對應 ip=IPAddress.Parse("127.0.0.1"); //可以是區域網或網際網路ip,此處是本機 ipEnd=new IPEndPoint(ip,6666); //伺服器埠號 //開啟一個執行緒連線,必須的,否則主執行緒卡死 connectThread=new Thread(new ThreadStart(SocketReceive)); connectThread.Start(); } void SocketConnet() { if(serverSocket!=null) serverSocket.Close(); //定義套接字型別,必須在子執行緒中定義 serverSocket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); print("ready to connect"); //連線 serverSocket.Connect(ipEnd); //輸出初次連線收到的字串 recvLen=serverSocket.Receive(recvData); recvStr=Encoding.ASCII.GetString(recvData,0,recvLen); print(recvStr); } public void SocketSend(string sendStr) { //清空傳送快取 sendData=new byte[1024]; //資料型別轉換 sendData=Encoding.ASCII.GetBytes(sendStr); //傳送 serverSocket.Send(sendData,sendData.Length,SocketFlags.None); } void SocketReceive() { SocketConnet(); //不斷接收伺服器發來的資料 while(true) { recvData=new byte[1024]; recvLen=serverSocket.Receive(recvData); if(recvLen==0) { SocketConnet(); continue; } recvStr=Encoding.ASCII.GetString(recvData,0,recvLen); print(recvStr); } } //返回接收到的字串 public string GetRecvStr() { string returnStr; //加鎖防止字串被改 lock(this) { returnStr=recvStr; } return returnStr; } public void SocketQuit() { //關閉執行緒 if(connectThread!=null) { connectThread.Interrupt(); connectThread.Abort(); } //最後關閉伺服器 if(serverSocket!=null) serverSocket.Close(); print("diconnect"); } }
using UnityEngine;
using System.Collections;
public class TcpTest:MonoBehaviour
{
string editString="hello wolrd"; //編輯框文字
GameObject cube;
TcpClientHandler tcpClient;
// Use this for initialization
void Start()
{
//初始化網路連線
//tcpClient=new TcpClientHandler(); //因為tcp的類繼承了monobehaviour所以不能用new,或者去掉對monobehaviour繼承就可以用new
tcpClient=gameObject.AddComponent<TcpClientHandler>();
tcpClient.InitSocket();
//找到cube
cube=GameObject.Find("Cube");
}
void OnGUI()
{
editString=GUI.TextField(new Rect(10,10,100,20),editString);
GUI.Label(new Rect(10,30,300,20),tcpClient.GetRecvStr());
if(GUI.Button(new Rect(10,50,60,20),"send"))
tcpClient.SocketSend(editString);
}
// Update is called once per frame
void Update()
{
if(tcpClient.GetRecvStr()!=null)
{
switch(tcpClient.GetRecvStr())
{
case "leftrotate":
cube.transform.Rotate(Vector3.up,50*Time.deltaTime);
break;
case "rightrotate":
cube.transform.Rotate(Vector3.down,50*Time.deltaTime);
break;
}
}
}
void OnApplicationQuit()
{
//退出時關閉連線
tcpClient.SocketQuit();
}
}
測試
程式實現服務端和客戶端互相收發訊息,服務端按鈕可以控制客戶端裡面的cube旋轉。