作者:蟑螂恶霸
链接:https://www.zhihu.com/question/381711152/answer/3083699647
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
本文将从以下几个方面来讲解Unity客户端网络架构的设计与实现:
1.网络通信协议的选择
2.客户端网络框架的设计
3.网络通信的实现
4.代码实现
对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的技术大佬,欢迎你来交流学习。
一、网络通信协议的选择
网络通信协议是实现客户端与服务器之间通信的基础,目前常用的网络通信协议有TCP和UDP两种。
TCP协议是面向连接的协议,具有可靠性高、稳定性好的特点,但是在传输大量数据时,效率较低。
UDP协议是无连接的协议,传输效率高,但是可靠性不如TCP。
在Unity客户端网络架构的设计中,需要根据实际情况选择合适的网络通信协议。如果是对网络传输的稳定性和可靠性有要求的游戏,如MOBA游戏,就需要选择TCP协议。如果是对网络传输的效率有要求的游戏,如射击游戏,就需要选择UDP协议。
二、客户端网络框架的设计
在Unity客户端网络架构的设计中,需要考虑以下几个方面:
1.网络连接的建立与断开
2.消息的发送与接收
3.消息的封装与解析
4.消息的处理与回调
网络连接的建立与断开
在客户端网络框架的设计中,需要实现网络连接的建立与断开。网络连接的建立需要指定服务器的IP地址和端口号,通过Socket类建立TCP或UDP连接。网络连接的断开需要释放Socket资源,关闭网络连接。
消息的发送与接收
在客户端网络框架的设计中,需要实现消息的发送与接收。消息的发送需要将消息封装成字节流,通过Socket类发送到服务器;消息的接收需要通过Socket类接收服务器发送的消息字节流,并将其解析成消息对象。
消息的封装与解析
在客户端网络框架的设计中,需要实现消息的封装与解析。消息的封装需要将消息对象转换成字节流,以便发送到服务器;消息的解析需要将服务器发送的字节流转换成消息对象,以便在客户端中进行处理。
消息的处理与回调
在客户端网络框架的设计中,需要实现消息的处理与回调。消息的处理需要根据消息类型进行相应的处理,例如登录消息需要进行账号验证;消息的回调需要在处理完消息后将处理结果返回给游戏逻辑层,以便进行相应的处理。
三、网络通信的实现
在Unity客户端网络架构的实现中,需要使用Socket类进行网络通信。Socket类是.NET Framework提供的用于网络通信的基础类,支持TCP和UDP两种协议。
在使用Socket类进行网络通信时,需要考虑以下几个方面:
1.网络连接的建立与断开
2.消息的发送与接收
3.消息的封装与解析
4.消息的处理与回调
网络连接的建立与断开
使用Socket类建立网络连接的方法如下:
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ip, port);
使用Socket类断开网络连接的方法如下:
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ip, port);
消息的发送与接收
使用Socket类发送消息的方法如下:
byte[] bytes = message.ToBytes();
socket.Send(bytes);
使用Socket类接收消息的方法如下:
byte[] bytes = new byte[1024];
int length = socket.Receive(bytes);
Message message = Message.Parse(bytes, length);
消息的封装与解析
消息的封装需要将消息对象转换成字节流,以便发送到服务器。消息的解析需要将服务器发送的字节流转换成消息对象,以便在客户端中进行处理。消息的封装与解析可以通过序列化和反序列化来实现。
消息的处理与回调
消息的处理需要根据消息类型进行相应的处理,例如登录消息需要进行账号验证。消息的回调需要在处理完消息后将处理结果返回给游戏逻辑层,以便进行相应的处理。
四、代码实现
以下是一个简单的Unity客户端网络框架的实现,使用TCP协议进行网络通信。该框架实现了网络连接的建立与断开、消息的发送与接收、消息的封装与解析、消息的处理与回调等功能。
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
public class Client
{
private Socket socket;
private byte[] buffer;
public bool Connect(string ip, int port)
{
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ip, port);
buffer = new byte[1024];
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, null);
return true;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return false;
}
}
public void Disconnect()
{
if (socket != null && socket.Connected)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
public void Send(Message message)
{
byte[] bytes = message.ToBytes();
socket.Send(bytes);
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
int length = socket.EndReceive(ar);
if (length > 0)
{
Message message = Message.Parse(buffer, length);
OnMessageReceived(message);
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, null);
}
else
{
Disconnect();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Disconnect();
}
}
private void OnMessageReceived(Message message)
{
switch (message.Type)
{
case MessageType.Login:
LoginMessage loginMessage = (LoginMessage)message;
bool success = Login(loginMessage.Username, loginMessage.Password);
ResponseMessage responseMessage = new ResponseMessage(success);
Send(responseMessage);
break;
case MessageType.Move:
MoveMessage moveMessage = (MoveMessage)message;
Move(moveMessage.Direction);
break;
}
}
private bool Login(string username, string password)
{
// TODO: login logic
return true;
}
private void Move(int direction)
{
// TODO: move logic
}
}
public enum MessageType
{
Login,
Move
}
public abstract class Message
{
public MessageType Type { get; protected set; }
public abstract byte[] ToBytes();
public static Message Parse(byte[] bytes, int length)
{
MessageType type = (MessageType)BitConverter.ToInt32(bytes, 0);
switch (type)
{
case MessageType.Login:
return LoginMessage.Parse(bytes, length);
case MessageType.Move:
return MoveMessage.Parse(bytes, length);
default:
throw new ArgumentException("Invalid message type");
}
}
}
public class LoginMessage : Message
{
public string Username { get; private set; }
public string Password { get; private set; }
public LoginMessage(string username, string password)
{
Type = MessageType.Login;
Username = username;
Password = password;
}
public override byte[] ToBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(BitConverter.GetBytes((int)Type));
bytes.AddRange(BitConverter.GetBytes(Username.Length));
bytes.AddRange(System.Text.Encoding.UTF8.GetBytes(Username));
bytes.AddRange(BitConverter.GetBytes(Password.Length));
bytes.AddRange(System.Text.Encoding.UTF8.GetBytes(Password));
return bytes.ToArray();
}
public static LoginMessage Parse(byte[] bytes, int length)
{
int offset = 4;
int usernameLength = BitConverter.ToInt32(bytes, offset);
offset += 4;
string username = System.Text.Encoding.UTF8.GetString(bytes, offset, usernameLength);
offset += usernameLength;
int passwordLength = BitConverter.ToInt32(bytes, offset);
offset += 4;
string password = System.Text.Encoding.UTF8.GetString(bytes, offset, passwordLength);
return new LoginMessage(username, password);
}
}
public class MoveMessage : Message
{
public int Direction { get; private set; }
public MoveMessage(int direction)
{
Type = MessageType.Move;
Direction = direction;
}
public override byte[] ToBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(BitConverter.GetBytes((int)Type));
bytes.AddRange(BitConverter.GetBytes(Direction));
return bytes.ToArray();
}
public static MoveMessage Parse(byte[] bytes, int length)
{
int offset = 4;
int direction = BitConverter.ToInt32(bytes, offset);
return new MoveMessage(direction);
}
}
public class ResponseMessage : Message
{
public bool Success { get; private set; }
public ResponseMessage(bool success)
{
Type = MessageType.Response;
Success = success;
}
public override byte[] ToBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(BitConverter.GetBytes((int)Type));
bytes.AddRange(BitConverter.GetBytes(Success));
return bytes.ToArray();
}
public static ResponseMessage Parse(byte[] bytes, int length)
{
int offset = 4;
bool success = BitConverter.ToBoolean(bytes, offset);
return new ResponseMessage(success);
}
}
以上代码实现了一个简单的Unity客户端网络框架,使用TCP协议进行网络通信。在该框架中,客户端可以建立和断开网络连接,发送和接收消息,并对不同类型的消息进行相应的处理和回调。
作者:阿博的game圈
链接:https://www.zhihu.com/question/381711152/answer/2548719506
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
目前能做大型MMORPG ARPG的主流游戏服务器框架编程语言有:
C++:网络库: libevent, libuv, iocp/epoll, 可靠的UDP网络传送kcp;
序列化与反序列化:protobuf;
协议加密解密: crypto;
http解析库: curl;
数据库: mysql, redis, mongodb;
多线程: pthread库;
服务短寻路与导航: recastnavigation
行为决策树: 具体的库可以github搜索一下;
业务逻辑脚本开发: Lua等轻量级嵌入式脚本语言
Java:
网络库: Netty, Mina;
序列化与反序列化:protobuf;
协议加密解密: crypto;
http解析库: Java有很多这样的jar包,可以自行找下;
数据库: mysql, redis, mongodb;
多线程 Java Thread库;
服务短寻路与导航: recastnavigation Java版本
行为决策树: 也有Java版本;文章来源:https://www.toymoban.com/news/detail-717882.html
C#和Go不太熟悉,大家可以根据上面的清单自行去找一下。游戏服务端的框架还是建议自己写,自己粘合。服务器对稳定性要求很好, 所以还是自己写比较好。:文章来源地址https://www.toymoban.com/news/detail-717882.html
到了这里,关于unity 联网的游戏后端服务器框架和语言的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!