从零开始制作网络回合制战斗游戏(1)——网络通讯

从零开始制作网络回合制战斗游戏(1)------网络通讯

考虑到我们的教程是网络游戏,所以还是得先写个服务器,本篇教程会向你科普什么是网络通讯、实现网络通讯需要的工具protobuf及其使用方法,最后,其主要内容是教你运用这些知识来开发一个使用c#作为后端的服务器,并完成通讯功能

新建项目

新建一个unity项目作为客户端和c#控制台项目作为服务器

图片是做了一半后才后知后觉没写进教程的,多出来的文件不用在意,后面会说

Protobuf

简介

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API(即时通讯网注:Protobuf官方工程主页上显示的已支持的开发语言多达10种,分别有:C++、Java、Python、Objective-C、C#、JavaNano、JavaScript、Ruby、Go、PHP,基本上主流的语言都已支持,详见:github.com/52im/protob...

由于我个人目前不是很像另外开篇写protobuf的具体教程,所以这里先搬一篇知乎的教程来,后面会直接使用,观众们继续往后看就行zhuanlan.zhihu.com/p/141415216

导入Protobuf.dll

项目地址:github.com/ExcCoder/Pr...

这不是原项目,之前调的,介意的可以直接百度,注意本教程用的是3.0版本

这里运行build.bat就会编译ProtoFile下的test.proto文件到Target-CSSharpFile下,生成Test.cs,为了方便后续还会对bat进行修改

在unity的Asset目录创建一个Plug文件夹用于存放外部dll文件,将文件目录下的Google.Protobuf.dll复制进这里

服务端那边,右键依赖项,选择引用,点击下面的添加自,找到刚刚的Google.Protobuf.dll文件并选择

修改BuildAll.bat文件

用记事本或者vscode编辑build.bat,将下列代码复制到build.bat内,注意将服务器的项目地址和客户端的项目地址换成自己的

bat 复制代码
protoc --csharp_out=./Target-CSharpFile ProtoFile/test.proto
copy "./Target-CSharpFile/test.cs" "客户端目标目录"
copy "./Target-CSharpFile/test.cs" "服务器目标目录"
pause

这里建议服务器和客户端都创建一个proto文件夹来放协议

到此,protobuf的导入就算完成了

protobuf文件的编辑以及生成代码的讲解

进入ProtoFile/test.proto开始编辑我们的proto文件

这里的syntax是指当前的proto版本,我们这里是proto3,如果版本不一致在编译时会报错

这里主要的两种类型是enum和message,编译后会生成对于的enum内容和class内容,这点可以进我们刚刚输出的文件里查看

除了自定义的enum之外,proto还支持以下这些数据类型,详见官网

要注意的是这里的123这些序号必须一一对应,不能不按顺序的乱跳,否则会导致编译错误

服务端代码

前情提要:为了不让新手教程变得臃肿,本期只讲网络通讯,所以客户端和服务端的代码主要是通过socket和protobuf来实现网络通讯功能

1、新建一个Server类

csharp 复制代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Google.Protobuf;
using Protobufer;

namespace Net_Turn_Bases_Server
{
    public class Server
    {
    }
}

2、用socket创建一个监听Socket,用于监听客户端发来的数据

csharp 复制代码
private static void StartServer()
{
    // 创建监听 Socket
    _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    _listener.Bind(new IPEndPoint(IPAddress.Any, Port));
    _listener.Listen(10);

    Console.WriteLine($"服务开始,监听端口号 {Port}...");

     while (true)
     {
       // 接受客户端连接
       Socket clientSocket = _listener.Accept();
       HandleClient(clientSocket);
     }
}

这里的死循环是异步监听,接收到的消息为socket数据报,而对数据报的具体处理就是HandleClinet回调内Proto的活了

3、接收消息的回调

csharp 复制代码
private static void HandleClient(Socket clientSocket)
        {
            try
            {
                // 接收消息
                byte[] buffer = new byte[1024];
                int bytesRead = clientSocket.Receive(buffer);
                // 反序列化收到的 Protobuf 消息
                //这里将客户端接收的二进制数据转化为了MainPack实体类
                MainPack receivedMessage = MainPack.Parser.ParseFrom(buffer, 0, bytesRead);
                Console.WriteLine($"接收到信息: {receivedMessage.Str}");

                // 创建并发送响应消息
                MainPack responseMessage = new MainPack { Str = "Hello, Client!" };
                byte[] responseBuffer = responseMessage.ToByteArray();
                clientSocket.Send(responseBuffer);
                Console.WriteLine("Sent response.");

                // 关闭连接
                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error handling client: {ex.Message}");
            }
        }

由此,服务端的代码便完成了,具体实现了socket监听接收socket数据,再通过protobuf反序列化为MainPack实体类,并在运行结束后关闭了链接

客户端代码

新建一个空物体来放置客户端代码

具体逻辑

csharp 复制代码
using System.Net;
using System.Net.Sockets;
using Google.Protobuf;
using Protobufer;
using UnityEngine;

public class ProtoTest : MonoBehaviour
{
    private const int Port = 12345;
    private static Socket _clientSocket;

    public void Start()
    {
        StartClient();
    }

    private void StartClient()
    {
        // 创建客户端 Socket
        _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _clientSocket.Connect(new IPEndPoint(IPAddress.Loopback, Port));
        Debug.Log("连接到服务器");

        // 创建并发送消息
        //这里依然使用了protobuf将要发送的信息列化成二进制数据流
        MainPack message = new MainPack { Str = "Hello, Server!" };
        byte[] messageBuffer = message.ToByteArray();
        _clientSocket.Send(messageBuffer);
        Debug.Log("Sent message.");

        // 接收并反序列化响应消息
        byte[] responseBuffer = new byte[1024];
        int bytesRead = _clientSocket.Receive(responseBuffer);
        MainPack responseMessage = MainPack.Parser.ParseFrom(responseBuffer, 0, bytesRead);
       Debug.Log($"Received response: {responseMessage.Str}");

        // 关闭连接
        _clientSocket.Shutdown(SocketShutdown.Both);
        _clientSocket.Close();
    }
}

这样便完成了服务端和客户端的通讯

运行结果

先运行服务端再运行客户端,服务端运行后

客户端依次运行

相关推荐
古力德7 小时前
Unity中造轮子:定时器
c#·unity3d
Mapmost2 天前
【数据融合实战手册·进阶篇】模型融合总出错?先看看这些“对齐”了没!
unity3d
北桥苏4 天前
如何在 Unity3D 导入 Spine 动画
unity3d
Thomas游戏开发5 天前
Unity3D状态管理器实现指南
前端框架·unity3d·游戏开发
土豆宝10 天前
Unity Visual Scripting(可视化脚本) 自定义节点 踩坑教程
unity3d
Thomas游戏开发11 天前
Unity3D光照层级与动态切换指南
前端框架·unity3d·游戏开发
Thomas游戏开发22 天前
Unity3D 崩溃分析工具的集成与优化
前端框架·unity3d·游戏开发
Thomas游戏开发1 个月前
Unity3D网格简化与LOD技术详解
前端框架·unity3d·游戏开发
Thomas_YXQ1 个月前
Unity3D 图形渲染(Graphics & Rendering)详解
开发语言·unity·图形渲染·unity3d·shader
Thomas游戏开发1 个月前
Unity3D 图形渲染(Graphics & Rendering)详解
前端·unity3d·游戏开发