1. 程式人生 > >unity開發:Qt C++與unity之間TCP網路通訊

unity開發:Qt C++與unity之間TCP網路通訊

考慮實現用C++做伺服器,unity做客戶端實現TCP網路通訊。

以下采用TCP單執行緒連線。

Qt C++服務端

建立一個Qt的GUI專案,在介面上放一個label顯示連線狀態,兩個button作為指令傳送控制。

記得在pro檔案中加入network模組

widget.h

#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
widget.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");
}
main.cpp未更改就不貼了。

unity C#客戶端

建立一個unity場景,拖入一個cube

把tcpsocket連線部分封裝成了一個單獨的類TcpClientHandler,再加一個指令碼TcpTest掛到場景中,在這個指令碼中例項化用於連線的TcpClientHandler。

TcpClientHandler.cs

using 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");
    }

}
TcpTest.cs
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旋轉。