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

 

服务器的开发选择有很多:java + javascript, c+python, c+lua, scala, go, erlang。我们面向性能的服务器用 java,面向逻辑服务器 python,面向高并发的会选择 scala,早年开发游戏必须用C++,这没得说,2000-2004年,java还没有nio,其他动态语言不抗重负,只能C/C++能开发出完整可用的游戏服务端,下面我们来看下用C++采用udp开发一个简单的服务器与unity通信,众所周知UDP通信,发送速度快,更加灵活,UDP实际相当于是对等通信,不用建立连接,但是这里为了有个server的概念,在服务端绑定了端口,而客户端则是发送时随机分配的端口

C++服务端

建立gui工程,pro里面加入network模块,界面放一个label,两个button

widget.h


#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

private:
Ui::Widget *ui;
private:
QString statusText; //状态信息
QUdpSocket *udpSocket; //套接字
QHostAddress clientIp; //客户端ip
quint16 clientPort; //客户端port

void SocketSend(QString sendStr,QHostAddress targetIp,quint16 targetPort); //发送数据,可以向指定目标发送,或者广播
private slots:
void ProcessPendingDatagram(); //当接收到数据时作出响应
void on_leftBtn_clicked();
void on_rightBtn_clicked();
};

#endif // WIDGET_H

widget.cpp


#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//初始化udp
udpSocket=new QUdpSocket(this);
udpSocket->bind(QHostAddress::Any,8888);//绑定ip和端口,可以是Any,LocalHost
//label显示状态
statusText=statusText+"wait for connecting..."+"\n";
ui->statusLabel->setText(statusText);
//绑定信号槽,当接收到数据时作出反应
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(ProcessPendingDatagram()));
}

void Widget::ProcessPendingDatagram()
{
//等待数据接收完毕再做处理
while(udpSocket->hasPendingDatagrams())
{
QByteArray recvData;
recvData.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(recvData.data(),recvData.size(),&clientIp,&clientPort); //从发送方的包中读取数据以及ip和port并赋值给类的变量
statusText+="connet from "+clientIp.toString()+":"+QString::number(clientPort)+" ";
statusText+=recvData+"\n";
//显示到状态标签
ui->statusLabel->setText(statusText);
//转发回去
SocketSend("from server:"+recvData,clientIp,clientPort);
}
}

void Widget::SocketSend(QString sendStr,QHostAddress targetIp,quint16 targetPort)
{
udpSocket->writeDatagram(sendStr.toStdString().c_str(),sendStr.length(),targetIp,targetPort);
}

Widget::~Widget()
{
delete ui;
}

//unity物体左旋
void Widget::on_leftBtn_clicked()
{
SocketSend("leftrotate",clientIp,clientPort);
}

//unity物体右旋
void Widget::on_rightBtn_clicked()
{
SocketSend("rightrotate",clientIp,clientPort);
}

main.cpp未更改,不贴了

unity C#客户端

同样是吧udp socket部分封装成了一个类,加到另外一个类里面,挂到场景里

UdpClientHandler.cs


using UnityEngine;
using System.Collections;
//引入库
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class UdpClientHandler:MonoBehaviour
{
//以下默认都是私有的成员
Socket socket; //目标socket
EndPoint serverEnd; //服务端
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,局域网,互联网
ipEnd=new IPEndPoint(IPAddress.Parse("127.0.0.1"),8888);
//定义套接字类型,在主线程中定义
socket=new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
//定义服务端
IPEndPoint sender=new IPEndPoint(IPAddress.Any,0);
serverEnd=(EndPoint)sender;
print("waiting for sending UDP dgram");

//建立初始连接,这句非常重要,第一次连接初始化了serverEnd后面才能收到消息
SocketSend("hello");

//开启一个线程连接,必须的,否则主线程卡死
connectThread=new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}

public void SocketSend(string sendStr)
{
//清空发送缓存
sendData=new byte[1024];
//数据类型转换
sendData=Encoding.ASCII.GetBytes(sendStr);
//发送给指定服务端
socket.SendTo(sendData,sendData.Length,SocketFlags.None,ipEnd);
}

//服务器接收
void SocketReceive()
{
//进入接收循环
while(true)
{
//对data清零
recvData=new byte[1024];
//获取客户端,获取服务端端数据,用引用给服务端赋值,实际上服务端已经定义好并不需要赋值
recvLen=socket.ReceiveFrom(recvData,ref serverEnd);
print("message from: "+serverEnd.ToString()); //打印服务端信息
//输出接收到的数据
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();
}
//最后关闭socket
if(socket!=null)
socket.Close();
}

}

UdpTest.cs


using UnityEngine;
using System.Collections;

public class UdpTest:MonoBehaviour
{
string editString="hello wolrd"; //编辑框文字
GameObject cube;

UdpClientHandler udpClient;
// Use this for initialization
void Start()
{
//初始化网络
udpClient=gameObject.AddComponent<UdpClientHandler>();
udpClient.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),udpClient.GetRecvStr());
if(GUI.Button(new Rect(10,50,60,20),"send"))
udpClient.SocketSend(editString);
}

// Update is called once per frame
void Update()
{
if(udpClient.GetRecvStr()!=null)
{
switch(udpClient.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()
{
//退出时关闭连接
udpClient.SocketQuit();
}
}

测试

依然是服务端和客户端之间互相收发消息,服务端可以控制客户端里面的cube旋转

unity之间UDP网络通信

unity之间UDP网络通信

unity之间UDP网络通信