当前位置:首页 > CN2资讯 > 正文内容

unity服务器部署 unity服务器搭建

1天前CN2资讯



unity-学习笔记

  • 搭建服务器与客户端连接-方法一
  • 搭建服务器
  • 方法二


搭建服务器与客户端连接-方法一

我是看b站一个大佬的视频写的,链接如下


搭建服务器

在vs2017里创建新项目

新项目选择控制台应用程序

创建之后在解决方案里添加一些文件夹和类


大致如图

首先需要创建一个启动类

作用是启动服务器

添加while的目的是为了能让程序一直运行

class Program { static void Main(string[] args) { ServerSocket server = new ServerSocket(); server.StartConnect(); while (true) { } } }

然后创建一个服务器类
核心是通过socket进行通信
使用socket通信需要为socket设置一些参数才能成功通信
如ip和端口号
使用bind方法连接其他客户端
服务器的话还需要设置listen去监听客户端发送的消息
然后还需要使用accept接收客户端发送的socket实例,然后根据这个实例进行消息的获取,accept是非异步方法,所以当服务器进行到这个方法的时候会被阻塞,直到获得有客户端的socket才进行后面的内容

//创建构造函数 private string ip = "127.0.0.1"; private int port = 30000; //服务器socket private Socket socket; private IPEndPoint iep; private byte[] readBuffer = new byte[1024]; public void StartConnect() { try { //创建socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //打开服务器socket接收其他socket的权限 iep = new IPEndPoint(IPAddress.Parse(ip), port); //设置socket,和客户端的socket进行连接 socket.Bind(iep); //设置socket,一次性能同时访问服务器的客户端数量 socket.Listen(1000); //获得客户端的socket Console.WriteLine("服务器开启成功!"); while (true) { Console.WriteLine("出现客户端!"); Socket client = socket.Accept(); Player player = new Player(client);//把客户端的socket放在player类里进行管理 } } catch (Exception e) { Console.WriteLine("服务器开启失败"); Console.WriteLine("原因为" + e.Message); } }

创建一个单例类,当其他需要单例的类可以直接继承这个方法,然后就可以自动成为单例

public class SingleTon<T> where T :class,new () { private static T instance; public static T Instance { get { if (instance==null) { instance = new T(); } return instance; } } }

创建player类

public class Player:Client { public Player(Socket _socket):base(_socket) { } }

继承client,这样创建这个类就可以连带创建client类

创建client类

private Socket client; Thread thread; ThreadStart ts; private byte[] readBuffer;//获取到的字节流 int maxBuffer = 10240; private bool isRun = false; private int alread = 0;//已经读取的字节索引 private int userByte = 0;//已经使用的字节数 public Client(Socket _client) { client = _client; ts = new ThreadStart(run); thread = new Thread(ts); thread.IsBackground = true; thread.Start(); } public void run() { isRun = true; readBuffer = new byte[maxBuffer]; try { client.BeginReceive(readBuffer,0,maxBuffer,0,new AsyncCallback(Receive), client); Console.WriteLine(Encoding.UTF8.GetString(readBuffer)); } catch(Exception e) { Console.WriteLine("client的run方法"+e.Message); } /*while (isRun) { }*/ } /// <summary> /// 获得回调函数 /// </summary> /// <param name="ar"></param> private void Receive(IAsyncResult ar) { Socket socket = (Socket)ar.AsyncState; //在请求结束的时候获得请求的数据的字节数 int length = 0; try { length = socket.EndReceive(ar); } catch(Exception e) { Console.WriteLine("client的receive方法" + e.Message); } if (length>0) { alread += length; if (alread>4)//说明信息里包括头信息是有数据的 { Dispose(); } } try { client.BeginReceive(readBuffer, alread, maxBuffer, 0, new AsyncCallback(Receive), client); } catch(Exception e) { Console.WriteLine(e.Message); } } private void Dispose() { while (true) { try { //需要先获得请求头,根据头进行数据解析 byte[] head = new byte[4]; Buffer.BlockCopy(readBuffer, userByte, head, 0, 4);//获得从头信息之后开始的需要的数据 int length = BitConverter.ToInt32(head, 0); //Console.WriteLine("获得的头信息的长度为:"+length); if (alread >= userByte + length + 4) { byte[] temp = new byte[length]; Buffer.BlockCopy(readBuffer, userByte + 4, temp, 0, length); Console.WriteLine("有数据"); string content = Encoding.UTF8.GetString(temp); JsonData jd = JsonMapper.ToObject<JsonData>(content); if (jd != null) { //解析数据 DisposePacket(jd); } userByte = userByte + length + 4; if ((alread - userByte) >= 4) { continue; } else { if ((alread - userByte) <= 0) { readBuffer = new byte[maxBuffer]; } else { byte[] t = new byte[alread - userByte]; Buffer.BlockCopy(readBuffer, userByte, t, 0, t.Length); readBuffer = new byte[maxBuffer]; Buffer.BlockCopy(t, 0, readBuffer, 0, t.Length); } alread = alread - userByte; userByte = 0; break; } } } catch(Exception e) { Console.WriteLine("dispose方法出现了问题"+e.Message); } } } private void DisposePacket(JsonData jd) { Console.WriteLine("接收消息,内容为:" + jd.ToJson()); JsonData jd1 = new JsonData(); jd1["name"] = 2; jd1["user"] = "yes"; SendMessage(jd1); } private void SendMessage(JsonData jd) { byte[] content = Encoding.UTF8.GetBytes(jd.ToJson()); byte[] head = new byte[4]; head = BitConverter.GetBytes(content.Length); byte[] ss = new byte[head.Length+content.Length]; //把头信息写入ss字节里 Buffer.BlockCopy(head,0,ss,0,4); //把实际数据写入ss字节里,头信息的后面 Buffer.BlockCopy(content,0,ss,4,content.Length); client.Send(ss); }

client类里需要的方法有:
在构造函数里需要把方法写在里面,并且是通过开启多线程的方式,这样就可以让其他的客户端访问服务器了
对socket进行解析的方法
发送消息给客户端socket的方法
逻辑大概是:
获得客户端的socket,然后使用beginreceive方法获得socket里的字节流信息和回调函数
当有字节流信息的时候才对其进行解析
回调函数的作用就是当一次请求完毕之后才会执行的方法
在这个方法里写对数据的处理即可
这里在看教程的时候,教程说一般的服务器和客户端之间的通信都是需要有消息头的,这个的目的是为了在数据发送的时候对其进行判断,才能知道消息是什么类型,然后对应的消息类型去进行对应的方法处理,如登录、注册、装备信息等的处理方法都不同(猜想调用的mysql方法啥的是不一样的,所以才需要对其进行判断把~)
所以需要先对发送的字节流的头消息进行判断,当有头消息并且头消息后面还有数据的时候才接着走后面解析消息的方法
这里大佬对字节流的处理方式是使用Buffer.BlockCopy()
这个方法的好处是能对两个byte数组进行数据的替换
里面有五个参数
第一个是被复制的数组,第二个是从被复制的数组的哪个地方开始读取,第三个是要复制的数组,第四个是从要复制的数组的哪个地方开始,第五个是复制的长度

大佬还解决了当传送的消息不是连续的时候的问题
通过userbyte记录已经使用的字节,然后需要的时候从这个userbyte才开始读取数据
这里面的逻辑有点复杂,我理解了几天才大概明白这个流程
直接复制上面的代码是可以保证运行的,我也基本没改过代码,理解的话最好是自己敲一遍,我是这几天理解的时候敲了5、6次才对socket的通信有了比较清楚的认识
上面就只是讲重要的部分,后面再更新客户端的实现方法~

2021.6.15更新思路
如何在通过服务器的情况下添加用户角色到服务器里
个人推测是通过客户端返回的数据(用户角色的信息,如血量,体力,速度等属性)然后在服务器的场景中实例化角色,将属性赋值到对应的角色中,从而实现场景中添加用户角色,至于控制移动角色,这个还有待考证~

客户端的实现方法和服务器里的client方法如出一撤,区别就是
1需要连接的是服务器的ip
2然后需要对json数据进行解析的话,需要导入litjson.dll或者源码,通过jsonmapper对数据进行实例化即可~
3需要创建一个关闭socket的方法,否则当客户端的unity关闭的时候服务器会报错~

private static Socket client; private static Thread thread; private static ThreadStart ts; private static byte[] readBuffer;//获取到的字节流 private static int maxBuffer = 30000; private static bool isRun = false; private static int alread = 0;//已经读取的字节索引 private static int userByte = 0;//已经使用的字节数 public ClientDemo() { //client = _client; //ts = new ThreadStart(run); //thread = new Thread(ts); //thread.IsBackground = true; //thread.Start(); } public static void StartThread() { client = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"),30000); try { Debug.Log("链接服务器成功"); client.Connect(iep); } catch(Exception e) { Debug.Log("链接服务器失败"+e.Message); } ClientDemo.isRun = true; ClientDemo.readBuffer = new byte[ClientDemo.maxBuffer]; ClientDemo.ts = new ThreadStart(run); thread = new Thread(ts); thread.IsBackground = true; thread.Start(); } public static void run() { isRun = true; readBuffer = new byte[maxBuffer]; try { client.BeginReceive(readBuffer, 0, maxBuffer, 0, new AsyncCallback(Receive), client); Console.WriteLine(Encoding.UTF8.GetString(readBuffer)); } catch (Exception e) { Debug.Log("client的run方法"+e.Message); } /*while (isRun) { }*/ } /// <summary> /// 获得回调函数 /// </summary> /// <param name="ar"></param> private static void Receive(IAsyncResult ar) { Socket socket = (Socket)ar.AsyncState; //在请求结束的时候获得请求的数据的字节数 int length = 0; try { length = socket.EndReceive(ar); } catch (Exception e) { Console.WriteLine("client的receive方法" + e.Message); } if (length > 0) { alread += length; if (alread > 4)//说明信息里包括头信息是有数据的 { Dispose(); } } try { client.BeginReceive(readBuffer, alread, maxBuffer, 0, new AsyncCallback(Receive), client); } catch (Exception e) { Console.WriteLine(e.Message); } } private static void Dispose() { while (true) { try { //需要先获得请求头,根据头进行数据解析 byte[] head = new byte[4]; Buffer.BlockCopy(readBuffer, userByte, head, 0, 4);//获得从头信息之后开始的需要的数据 int length = BitConverter.ToInt32(head, 0); Console.WriteLine("获得的头信息的长度为:" + length); if (alread >= userByte + length + 4) { byte[] temp = new byte[length]; Buffer.BlockCopy(readBuffer, userByte + 4, temp, 0, length); string content = Encoding.UTF8.GetString(temp); JsonData jd = JsonMapper.ToObject<JsonData>(content); if (jd != null) { //解析数据 DisposePacket(jd); } userByte = userByte + length + 4; if ((alread - userByte) >= 4) { continue; } else { if ((alread - userByte) <= 0) { readBuffer = new byte[maxBuffer]; } else { byte[] t = new byte[alread - userByte]; Buffer.BlockCopy(readBuffer, userByte, t, 0, t.Length); readBuffer = new byte[maxBuffer]; Buffer.BlockCopy(t, 0, readBuffer, 0, t.Length); } alread = alread - userByte; userByte = 0; break; } } } catch (Exception e) { Console.WriteLine("dispose方法出现了问题" + e.Message); } } } private static void DisposePacket(JsonData jd) { //Console.WriteLine("接收消息,内容为:" + jd.ToJson()); Debug.Log(jd.ToJson()); } public static void SendMessage(JsonData jd) { byte[] content = Encoding.UTF8.GetBytes(jd.ToJson()); byte[] head = new byte[4]; head = BitConverter.GetBytes(content.Length); byte[] ss = new byte[head.Length + content.Length]; //把头信息写入ss字节里 Buffer.BlockCopy(head, 0, ss, 0, 4); //把实际数据写入ss字节里,头信息的后面 Buffer.BlockCopy(content, 0, ss, 4, content.Length); client.Send(ss); } public static void Close() { isRun = false; ClientDemo.thread.Abort(); ClientDemo.client.Close(); Debug.Log("关闭线程"); }

方法二

https://www.bilibili.com/video/BV1rJ411D7VJ

使用photon框架,最多支持20人在同一个房间里


    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/20714.html

    分享给朋友:

    “unity服务器部署 unity服务器搭建” 的相关文章

    搬瓦工退款政策全解析:30天无理由退款,轻松解决用户疑虑

    搬瓦工退款政策的基本介绍 搬瓦工(BandwagonHost)作为国内知名的VPS服务提供商,以其性价比高、服务稳定而受到许多用户的青睐。在使用过程中,用户可能会因为各种原因需要申请退款。搬瓦工提供了30天无理由退款保证,确保用户在购买后的一段时间内享有退款的权利。这一政策不仅体现了搬瓦工对用户权益...

    GCE教程:快速掌握基因组评估软件的安装与使用技巧

    1.1 GCE软件概述 GCE(Genome Characteristics Estimation)是一款由华大基因开发的基因组评估软件。它的主要功能是通过分析二代测序数据,评估基因组的特征,如基因组大小、杂合度等。GCE以其高效、准确的特点,成为基因组研究中不可或缺的工具之一。无论是科研人员还是生...

    如何在VPS上轻松部署和管理Telegram机器人 | 详细指南

    1.1 创建Telegram机器人 在Telegram上创建一个机器人非常简单。我们只需要与@BotFather进行对话。@BotFather是Telegram官方提供的机器人管理工具,专门用于创建和管理机器人。通过发送/newbot命令,我们可以开始创建自己的机器人。@BotFather会引导我们...

    探索香港节点的地理与经济优势及其全球数据传输作用

    香港节点的地理与经济优势 谈到香港的地理和经济优势,我总是想起它的独特地理位置。香港位于亚洲的心脏地带,紧密相连着中国大陆、东南亚、日本和韩国等区域。这些距离使得这里成为了数据流量的重要连接点。无论是企业还是个人,想要快速和高效地进行国际沟通时,香港总是首选的地方之一。作为一个全球重要的金融中心,香...

    探索宝塔的历史与文化:传承与创新的结合

    宝塔,这种在中国传统文化中扮演着重要角色的建筑,具有悠久而丰富的历史。我曾在一次旅行中惊叹于那些巍峨耸立的宝塔,仿佛它们在诉说着古老的故事。它们的起源可以追溯到佛教传入中国之前,实际上,宝塔最早的样式源自印度,梵语中称之为“窣堵坡”,主要用于供奉佛陀的舍利和进行宗教仪式。 在东汉时期,佛教逐步传入中...

    服务器租赁指南:如何选择适合的云服务和价格

    对于很多企业和个人用户来说,服务器租赁是一个非常实用的选择。简单来说,服务器租赁就是用户向服务器提供商支付费用,然后获得在一定时间内使用服务器的权利。这样一来,用户就无需花费时间和金钱去购买和维护物理服务器,可以迅速开始在线业务。 当我第一次接触服务器租赁时,发现这一服务的便利性令我十分惊讶。传统的...