最近看了下关于unity的网络模块netwrok即将淘汰,思索了下准备自己用socket去封装个局域网的通讯插件

看了一下关于同步问题

https://www.jianshu.com/p/fbd8eda9df62

然后局域网游戏,外挂基本上靠的是玩家自觉,而且也不需要自己搞个服务器

  • 房间系统:房主是服务器也是客户端

  • 随机数的生成保持一至:统一的随机种子(并且要保持种子的调用次数一至)

  • 连接后玩家操作通讯只发送操作指令

  • 每隔一段时间进行状态同步(针对每个需要同步的组件数据写一个组件)

实现步骤:

1、实现连接与通讯

2、制作根据业务需求的封装

首先第一步:实现连接与通讯

该组件能做最基础的房间系统和聊天室,稍微封装可以满足各种需求

当然胡来也是会有bug的,因为没有真正去用过

组件GameNetWorkManager作为Unity访问的核心类

//连接相关API

ServerDataList:可以获取局域网内的所有服务器数据

CreateServer:传进服务器名字和最大连接数,可以创建一个服务器

ConnectServer:传进服务器数据,可以连接服务器

//通讯相关API

OnReceive(事件):绑定该事件后,服务器接受到消息时将获取该消息,即收到消息时回调

SendData:发送给服务器的内的所有客户端

代码:

一共两个文件

GameNetWorkManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameNetWorkManager : MonoBehaviour
{private GameClient gameClient;private ServerData serverData;private GameServer gameServer;private ServerDataList serverDataList;public ServerData ServerData { get => serverData; }public GameClient GameClient { get => gameClient; }public GameServer GameServer { get => gameServer; }public ServerDataList ServerDataList { get => serverDataList; }/// <summary>/// 当收到服务器消息时/// </summary>public event System.Action<string> OnReceive;/// <summary>/// 创建服务器/// </summary>/// <param name="serverName"></param>/// <param name="maxNumber"></param>public void CreateServer(string serverName, int maxNumber){serverData = new ServerData(serverName, maxNumber);gameServer = new GameServer(serverData);gameClient.ConnectServer(serverData, (ServerData ServerData) =>{//初始化随机种子Random.InitState(ServerData.randomSeed);},(string message) =>{OnReceive(message);});}/// <summary>/// 连接服务器/// </summary>/// <param name="serverData"></param>public void ConnectServer(ServerData serverData){gameClient.ConnectServer(serverData, (ServerData ServerData) =>{//初始化随机种子Random.InitState(ServerData.randomSeed);},(string message) =>{OnReceive(message);});}public void SendData(string message){gameClient.SendMessageData(message);}private void Awake(){//创建客户端gameClient = new GameClient();//扫描服务器(即收到广播就进行数据更新)GameClient.CheckOutServerList((ServerDataList serverDataList) =>{this.serverDataList = serverDataList;});}private void OnDestroy(){if (gameClient != null){gameClient.Close();}if (gameServer != null){gameServer.Close();}}
}

GameNetWork.cs

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;[System.Serializable]
public class ServerDataList
{public List<ServerData> list = new List<ServerData>();internal void Add(ServerData serverData){list.Add(serverData);}internal void Remove(ServerData serverData){list.RemoveAll((ServerData item) =>{return serverData.IP == item.IP && serverData.port == item.port;});}internal void Update(ServerData serverData){foreach (var item in list){if (serverData.IP == item.IP && serverData.port == item.port){item.name = serverData.name;item.number = serverData.number;item.maxNumber = serverData.maxNumber;}}}
}[System.Serializable]
public class ServerData
{public string name;public int number;public int maxNumber;public string IP;public int port;/// <summary>/// 随机种子/// </summary>public int randomSeed;public ServerData(){randomSeed =  new System. Random().Next(0, 99999);}public ServerData(string name, int maxNumber){this.name = name;this.maxNumber = maxNumber;randomSeed = new System.Random().Next(0, 99999);}
}
/// <summary>
/// 游戏数据包
/// </summary>
[System.Serializable]
public struct GameNetPacket
{public string name;public string data;
}public interface IGameNetWorkBase
{void Close();
}public interface IGameServer : IGameNetWorkBase
{}
public interface IGameClient : IGameNetWorkBase
{void CheckOutServerList(Action<ServerDataList> OnCheckOut);void ConnectServer(ServerData serverData, Action<ServerData> OnConnect, Action<string> OnReceive);void SendMessageData(string data);
}
/// <summary>
/// 游戏服务器
/// </summary>
public class GameServer : IGameServer
{public ServerData serverData;/// <summary>/// 服务器套接字/// </summary>Socket ServerSocket;/// <summary>/// 客户端的套接字/// </summary>List<Socket> ClientSockets = new List<Socket>();const int minPort = 2725;const int maxPort = 2730;const int radiateMinPort = 2730;const int radiateMaxPort = 2735;private bool open;/// <summary>/// 循环客户端的信息/// </summary>/// <param name="socket"></param>private void LoopClientMessage(Socket socket){ClientSockets.Add(socket);//监听客户端发送信息,并且进行广播new Thread(() =>{while (open){try{byte[] receive = new byte[1024];//等待获取信息socket.Receive(receive);//进行广播发送foreach (var item in ClientSockets){item.Send(receive);}}catch (Exception){Debug.Log("断开连接...");Close();}}}).Start();}public GameServer(ServerData serverData){this.serverData = serverData;open = true;//服务器初始化// 侦听所有网络客户接口的客活动//使用指定的地址簇协议、套接字类型和通信协议ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);BindServerSocket(minPort);//设定最多玩家数量ServerSocket.Listen(this.serverData.maxNumber);// string ipStr = ipAddress.ToString();Debug.Log("服务器初始化完成");SendServerDataToAll("UpdateServer");//接受广播信息Socket receiveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);IPEndPoint sep = BindReceiverSocket(receiveSocket, radiateMinPort);EndPoint ep = (EndPoint)sep;//接收广播线程new Thread(() =>{while (open){byte[] message = new byte[1024];//设置缓冲数据流receiveSocket.ReceiveFrom(message, ref ep);//接收数据,并确把数据设置到缓冲流里面string data = Encoding.Unicode.GetString(message).TrimEnd('\u0000');GameNetPacket receiveData = JsonUtility.FromJson<GameNetPacket>(data);switch (receiveData.name){case "Is Room?":GameNetPacket gameNetPacket = new GameNetPacket();gameNetPacket.name = "NewServer";gameNetPacket.data = JsonUtility.ToJson(serverData);SendReceiveTo(JsonUtility.ToJson(gameNetPacket));break;default:break;}}}).Start();//监听客户端连接线程new Thread(() =>{while (open){try{//与客户的排队建立连接(堵塞)Socket socket = ServerSocket.Accept();LoopClientMessage(socket);Debug.Log("连接成功");进行广播更新房间信息SendServerDataToAll("UpdateServer");}catch (Exception){Debug.Log("停止等待客户端连接");open = false;}}}).Start();}private void SendServerDataToAll(string receiveType){GameNetPacket gameNetPacket = new GameNetPacket();gameNetPacket.name = receiveType;gameNetPacket.data = JsonUtility.ToJson(serverData);SendReceiveTo(JsonUtility.ToJson(gameNetPacket));}private static void SendReceiveTo(string data){//发送广播等待传输回来房间信息//初始化一个发送广播和指定端口的网络端口实例Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//设置该scoket实例的发送形式sendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);for (int i = radiateMinPort; i < radiateMaxPort; i++){IPEndPoint iep = new IPEndPoint(IPAddress.Broadcast, i);byte[] buffer = Encoding.Unicode.GetBytes(data);//发送sendSocket.SendTo(buffer, iep);}}private static IPEndPoint BindReceiverSocket(Socket receiveSocket, int port){IPEndPoint sep = null;try{sep = new IPEndPoint(IPAddress.Any, port);//初始化一个侦听局域网内部所有IP和指定端口receiveSocket.Bind(sep);//绑定这个实例}catch (Exception){port++;if (port < radiateMaxPort){BindReceiverSocket(receiveSocket, port);}else{throw;}}return sep;}private void BindServerSocket(int port){try{IPEndPoint IPEndPoint = new IPEndPoint(IPAddress.Any, port);//绑定IP地址和端口号ServerSocket.Bind(IPEndPoint);string hostName = Dns.GetHostName();   //获取本机名IPAddress[] localhosts = Dns.GetHostAddresses(hostName);foreach (var item in localhosts){if (item.ToString().IndexOf("192.168") != -1){serverData.IP = item.ToString();break;}}serverData.port = port;}catch (Exception){port++;if (port < maxPort){BindServerSocket(port);}else{throw;}}}public void Close(){open = false;//发送关闭房间信号GameNetPacket gameNetPacket = new GameNetPacket();gameNetPacket.name = "CloseServer";gameNetPacket.data = JsonUtility.ToJson(serverData);SendReceiveTo(JsonUtility.ToJson(gameNetPacket));//关闭服务器socketServerSocket.Close();//关闭所有客户端的socketforeach (var item in ClientSockets){item.Close();}}
}/// <summary>
/// 客户端  封装部分
/// </summary>
public partial class GameClient
{/// <summary>/// 广播端口/// </summary>const int receiveMinPort = 2730;const int receiveMaxPort = 2735;/// <summary>/// 广播接收socket/// </summary>private Socket radiateSocket;/// <summary>/// 客户端的socket/// </summary>private Socket clientSocket;private EndPoint ep;private bool openCheckOutServer = true;/// <summary>/// 广播发送一条信息/// </summary>/// <param name="data"></param>private static void SendReceiveTo(string data){//发送广播等待传输回来房间信息//初始化一个发送广播和指定端口的网络端口实例Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//设置该scoket实例的发送形式sendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);for (int i = receiveMinPort; i < receiveMaxPort; i++){IPEndPoint iep = new IPEndPoint(IPAddress.Broadcast, i);byte[] buffer = Encoding.Unicode.GetBytes(data);//发送sendSocket.SendTo(buffer, iep);}}private void CloseCheckOutServer(){openCheckOutServer = false;radiateSocket.Close();radiateSocket = null;}private static IPEndPoint BindReceiverSocket(Socket receiveSocket, int port){IPEndPoint sep = null;try{sep = new IPEndPoint(IPAddress.Any, port);//初始化一个侦听局域网内部所有IP和指定端口receiveSocket.Bind(sep);//绑定这个实例}catch (Exception){port++;if (port < receiveMaxPort){BindReceiverSocket(receiveSocket, port);}else{throw;}}return sep;}
}/// <summary>
/// 客户端 接口实现部分
/// </summary>
public partial class GameClient : IGameClient
{private bool connecting;public GameClient(){radiateSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//初始化一个Scoket协议IPEndPoint sep = BindReceiverSocket(radiateSocket, receiveMinPort);//初始化一个侦听局域网内部所有IP和指定端口ep = (EndPoint)sep;//绑定这个实例}/// <summary>/// 查看服务器列表/// </summary>/// <param name="OnCheckOut"></param>public void CheckOutServerList(Action<ServerDataList> OnCheckOut){openCheckOutServer = true;Debug.Log("查找房间");//广播发送一个数据SendReceiveTo(JsonUtility.ToJson(new GameNetPacket() { name = "Is Room?", data = "" }));ServerDataList serverDataList = new ServerDataList();new Thread(() =>{while (openCheckOutServer){try{byte[] message = new byte[1024];//设置缓冲数据流radiateSocket.ReceiveFrom(message, ref ep);//接收数据,并确把数据设置到缓冲流里面string json = Encoding.Unicode.GetString(message).TrimEnd('\u0000');GameNetPacket gameNetPacket = JsonUtility.FromJson<GameNetPacket>(json);switch (gameNetPacket.name){case "NewServer":serverDataList.Add(JsonUtility.FromJson<ServerData>(gameNetPacket.data));break;case "CloseServer":serverDataList.Remove(JsonUtility.FromJson<ServerData>(gameNetPacket.data));break;case "UpdateServer":serverDataList.Update(JsonUtility.FromJson<ServerData>(gameNetPacket.data));break;default:break;}OnCheckOut(serverDataList);}catch (Exception){throw;}}}).Start();}/// <summary>/// 关闭客户端/// </summary>public void Close(){if (openCheckOutServer){CloseCheckOutServer();}if (connecting){CloseConnecting();}}private void CloseConnecting(){connecting = false;clientSocket.Close();}public void SendMessageData(string data){if (clientSocket == null){Debug.Log("未连接服务器");return;}byte[] message = Encoding.UTF8.GetBytes(data);  //通信时实际发送的是字节数组,所以要将发送消息转换字节clientSocket.Send(message);}/// <summary>/// 连接至服务器/// </summary>/// <param name="serverData"></param>/// <param name="OnConnect"></param>/// <param name="OnReceive"></param>public void ConnectServer(ServerData serverData, Action<ServerData> OnConnect, Action<string> OnReceive){clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//使用指定的地址簇协议、套接字类型和通信协议IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(serverData.IP), serverData.port); // 用指定的ip和端口号初始化IPEndPoint实例clientSocket.Connect(serverEndPoint);new Thread(() =>{connecting = true;byte[] receive = new byte[1024];while (connecting){Debug.Log("等待消息");int length = clientSocket.Receive(receive);  // length 接收字节数组长度OnReceive(Encoding.UTF8.GetString(receive));}}).Start();}}

基础的unity局域网游戏框架(一)相关推荐

  1. 基础知识漫谈(3) 组合基础知识,设计游戏框架

    如何让画面动起来? 都知道动画的原理,想想看跑马灯,在任意时刻把跑马灯按停,面对眼睛的那附图,它叫做帧(Frame). 帧,就是一串儿连贯动画里的单个截面. 玩儿游戏的人知道,要达到基本流畅的水准,3 ...

  2. Unity Unet(四)多人在线游戏框架

    准备设置 打开项目后,进入Window/Services视图. 创建或登录项目ID进入下面面板. 选择Multiplayer进入 选择GoToDashboard进入网页(登录Unity账号) 选择下方 ...

  3. Unity 3D游戏开发项目《战斗吧!勇士》

    目录 版权声明:本博客涉及的内容是对本人游戏作品<战斗吧!勇士>项目的总结,发布在网络上,旨在大家交流学习.互相促进.严禁用于其他一切用途. 摘要 游戏开发技术概述 Unity 3D 游戏 ...

  4. 《分布式虚拟现实系统(DVR)》(Yanlz+Unity+SteamVR+分布式+DVR+人工智能+边缘计算+人机交互+云游戏+框架编程+立钻哥哥+)

    <分布式虚拟现实系统(DVR)> <分布式虚拟现实系统(DVR)> 版本 作者 参与者 完成日期 备注 YanlzVR_DVR_V01_1.0 严立钻 2019.07.11 # ...

  5. Unity联网游戏基础原理与字节数组

    如果你要制作一个能联网的游戏,无论是网络游戏还是局域网游戏都离不开网络通信,网络游戏一般是一个服务器对应多个客户端,如图: 多个玩家访问一台服务器,每个玩家实际上是一个客户端,在这里我们列举两个小例子 ...

  6. Unity 游戏框架搭建 (九) 减少加班利器-QConsole

    为毛要实现这个工具? 在我小时候,每当游戏在真机运行时,我们看到的日志是这样的. 没高亮啊,还有乱七八糟的堆栈信息,好干扰日志查看,好影响心情. 还有就是必须始终连着usb线啊,我想要想躺着测试... ...

  7. 我们来用Unity做一个局域网游戏(下)

    大家好,我又来了. 废话不多说,咱们赶紧的,接着上一篇文章把这个联网项目搞完. 客户端发送消息 然后在NetworkClient中提供发送消息的方法,发送消息使用消息队列的机制(就是把给发送的消息放进 ...

  8. Unity 游戏框架搭建 2018 (四) 我所理解的框架

    前言 架构和框架这些概念听起来很遥远,让很多初学者不明觉厉.会产生"等自己技术牛逼了再去做架构或者搭建框架"这样的想法.在这里笔者可以很肯定地告诉大家,初学者是完全可以去做这些事情 ...

  9. swift语言注册非免费苹果账号iOS游戏框架Sprite Kit基础教程

    swift语言注册非免费苹果账号iOS游戏框架Sprite Kit基础教程 1.2.3  注册非免费苹果账号swift语言注册非免费苹果账号iOS游戏框架Sprite Kit基础教程 免费的苹果账号在 ...

最新文章

  1. 第一次react-native项目实践要点总结
  2. linux 新增网络接口,为网络接口添加多IP - CentOS 7系统配置上的变化解析_Linux教程_Linux公社-Linux系统门户网站...
  3. .Net之配置文件自定义
  4. bp 预测 matlab代码,bp神经网络进行交通预测的Matlab源代码.docx
  5. [转载] Java static关键字详解
  6. 面向对象高级编程(5)-使用元类
  7. catia中的螺旋伞齿轮画法_使用CATIA绘制斜齿轮(直齿轮)得画法教程.doc
  8. ev3编程和python编程哪个好_上海市Python编程课程怎么选机构,ev3编程课程怎么选机构...
  9. 计算机右键 管理,鼠标右键菜单管理方法?
  10. android镜像_如何在任何Windows PC上镜像和控制您的Android手机
  11. Eterm系统出-航空公司大系统-PID放大软件-IBE查询接口 QQ 799670351 15075679773
  12. 微信开发 · 缓存问题
  13. Python Tic Tac Toe游戏
  14. Chrome打包扩展程序错误,清单文件缺失或不可读
  15. 腾讯技术分享:微信小程序音视频技术背后的故事
  16. C语言基础-#include<stdio.h>
  17. volatile作用
  18. 量化交易 第十二课 因子数据处理之市值中性化
  19. js 前端 时间戳转字符串 2019-08-18T16:00:00.000Z 转换 2019-08-19 00:00:00
  20. 1992年的鹿鼎记台词,嘛的,哥20年后才看到

热门文章

  1. TextViewVertical实现文字并列竖排 如古诗,蒙古语等
  2. SQL每日练习记录(MySQL)
  3. BATJ都爱问的Java多线程面试题整理
  4. 基于TDOA的Chan氏算法代码解释
  5. OBS Studio导播台多画面使用实测
  6. 高级篇之导播对讲Tally灯控多机位集成通信方案
  7. 域控制器DCU诊断设计
  8. 虚拟化设备-Virtio介绍
  9. 计算机网络面试的基本问题
  10. java 命令行 配置文件_命令行玩JAVA : 完善ANT的配置文件