Unity教程之-C++服务器与unity之间TCP网络通信

 

上篇文章我们了解了《C++服务器与unity之间UDP网络通信》,本篇文章我们继续来学习下利用tcp协议来实现C++服务器与unity之间的通信,以下采用TCP单线程连接。

C++服务端

建立一个Qt的GUI项目,在界面上放一个label显示连接状态,两个button作为指令发送控制。

记得在pro文件中加入network模块


#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旋转。

C++服务器与unity之间TCP网络通信

C++服务器与unity之间TCP网络通信

C++服务器与unity之间TCP网络通信

好了,本篇unity3d教程到此结束,下篇我们再会!