最近正在用Unity做一个类似三国杀的卡牌游戏,网上也找不到什么东西参考,只好自己动手一点点解决问题,由于服务器不是很熟,于是决定一边学Photon一边先做个局域网联机的玩玩,局域网联机我决定参考CS红警等游戏的模式先做个大厅一人建主其他人加入这种,毕竟直接输ip什么的太low了。

局域网联机说白了就是建主主机既是客户端也是服务器端,我选择选用UDP广播搞定大厅的问题,在用TCP连接主机和服务器。

大概流程如下:

1、 一个客户端创建房间,成为服务器端,开始监听;

2、客户端点击局域网联机后向全网广播UDP请求服务器端,

3、服务器收到消息,向客户端发送IP及端口号(房间信息,比如当前人数)

4、客户端接收并将收到的服务器房间显示在大厅中

5、客户端加入房间,和服务器建立TCP连接

之前为了逻辑清楚还在文档里画了图也放这里吧

这里的所有UDP都默认开启监听

这里的Server端同时也要创建TCPServer准备进行和客户端的正式连接

以下是测试图

输入姓名确认然后点创建

building好新开一个,点一下刷新

点加入

服务器客户端显示都如上图,这时候服务器和客户端都已经建立好连接了,开始游戏上注册上发送方法就行了,方法什么的都简单的封装过了,局域网亲测没问题。不过这里的可能会有一些小问题或者没做的比如返回关闭页面什么的,其实后来已经做了,但是游戏进度做了一些,删东西太麻烦就直接用之前的了,整体没啥问题的。

注意:如果是在同一主机测试,要把场景中EthernetManager上的端口改一下,保证打开的每一个程序的端口号不一样,多个主机就不用了,原因是UDP广播固定端口号,但是单机测试用回环ip,一个端口是不能开两个udpSocket的,默认广播端口9876,可以自己在代码改,如果单机测试记得用9876的程序来建主

EthernetManager中的代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Threading;
using System.Net.Sockets;
using LitJson;
using System.Text;
using System;public class EthernetManager : MonoBehaviour
{private static EthernetManager _instance;public static EthernetManager instance{get{return _instance;}set{_instance = value;}}private SocketClient client = null;private SocketTcpServer tcpServer = null;private SocketTcpClient tcpClient = null;public List<RoomInfo> roomList = new List<RoomInfo>();public Dictionary<int, Client> ClientList = new Dictionary<int, Client>();public List<RoomMemberInfo> rmiList = new List<RoomMemberInfo>();public EndPoint tempRoomEp = null;EndPoint host = null;public Thread t = null;public Thread l = null;public Thread tcpListen = null;public Thread clientListen = null;public int port;public bool shutClientListen = false;public string clientReceiveMessage = null;public string name = "";public Action<List<RoomMemberInfo>> ReceiveAction;void Awake(){instance = this;string hostName = Dns.GetHostName();IPAddress ipaddress = Dns.GetHostAddresses(hostName)[1];host = new IPEndPoint(ipaddress, port);}# region UDP客户端/// <summary>/// 创建客户端/// </summary>public void CreateSocket(){client = new SocketClient(host);}/// <summary>/// 监听服务器的消息/// </summary>public void ListenServerResponse(){Thread l = new Thread(client.ReceiveRoomInfo);l.IsBackground = true;l.Start();Debug.Log("UDP客户端开始监听");RequestEthernetServer();}/// <summary>/// 请求当前局域网中的服务器/// </summary>public void RequestEthernetServer(){client.SendRequest(port);}#endregion#region UDP 服务端/// <summary>/// 开始监听客户端请求/// </summary>public void ListenClientRequest(){t = new Thread(client.BackRoomInfo);t.IsBackground = true;t.Start();Debug.Log("开始监听");}#endregion#region TCP客户端public void CreateTcpClient(){if (tempRoomEp != null)tcpClient = new SocketTcpClient(tempRoomEp, host);tempRoomEp = null;}public void ConnectTcpServer(){tcpClient.Connect();clientListen = new Thread(tcpClient.Receive);clientListen.IsBackground = true;clientListen.Start();RequestRoomInfo();}public void RequestRoomInfo(){tcpClient.SendMessage(EthernetOrder.RequestMemberInfo.ToString() + ":" + name);}#endregion#region TCP服务端public void CreateTcpServer(){tcpServer = new SocketTcpServer(host);tcpListen = new Thread(tcpServer.Listen);tcpListen.IsBackground = true;tcpListen.Start();}public void BroadRoomInfo(){RoomMemberInfo info = new RoomMemberInfo(){id = 0,name = name,otherInfo = ""};rmiList.Add(info);foreach (var item in ClientList){info = new RoomMemberInfo(){id = item.Key,name = item.Value.username,otherInfo = ""};rmiList.Add(info);}ReceiveAction(rmiList);string json = JsonMapper.ToJson(rmiList);Debug.Log(json);BroadCast(json);}/// <summary>/// 广播/// </summary>public void BroadCast(string str){foreach (Client client in ClientList.Values){client.SendMessage(str);}}#endregionvoid OnDestroy(){if (t != null)t.Abort();if (l != null)l.Abort();if (tcpListen != null)tcpListen.Abort();if (clientListen != null)clientListen.Abort();if (client != null)client.Close();foreach (Client item in ClientList.Values){item.ShutDown();}}public void Reset(){if (t != null)t.Abort();if (l != null)l.Abort();if (tcpListen != null)tcpListen.Abort();if (clientListen != null)clientListen.Abort();if (client != null)client.Close();foreach (Client item in ClientList.Values){item.ShutDown();}/*client.Close();tcpServer.Close();tcpClient.Close();*/}
}

接收发送以及Socket创建监听什么的都被我分装到了类里

UDP

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Text;
using LitJson;
using System.Threading;public class SocketClient
{public string name = "kzg";Socket udpSocket = null;List<EndPoint> clientIplist = new List<EndPoint>();public EndPoint host;public int defaultPort = 9876;#region 客户端/// <summary>/// 创建UDPSocket/// </summary>/// <param name="port"></param>public SocketClient(EndPoint host){udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);udpSocket.Bind(host);this.host = host;}/// <summary>/// 发送请求服务器广播/// </summary>/// <param name="port"></param>public void SendRequest(int port){string message = "RequestServer";byte[] data = Encoding.UTF8.GetBytes(message);Debug.Log(message);EndPoint ip = new IPEndPoint(IPAddress.Broadcast, defaultPort);udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);         //注释记住udpSocket.SendTo(data, ip);}/// <summary>/// 接收房间信息/// </summary>/// <returns></returns>public void ReceiveRoomInfo(){EndPoint remote = new IPEndPoint(IPAddress.Any, 0);while (true){byte[] data = new byte[1024];int length = udpSocket.ReceiveFrom(data, ref remote);string s = Encoding.UTF8.GetString(data, 0, length);Debug.Log(s);if (!s.Equals("RequestServer")){RoomInfo info = LitJson.JsonMapper.ToObject<RoomInfo>(s);if (EthernetManager.instance.roomList.Count == 0)EthernetManager.instance.roomList.Add(info);for (int i = 0; i < EthernetManager.instance.roomList.Count; i++){if (EthernetManager.instance.roomList[i].Ipport == info.Ipport){EthernetManager.instance.roomList.Remove(EthernetManager.instance.roomList[i]);EthernetManager.instance.roomList.Add(info);break;}EthernetManager.instance.roomList.Add(info);}}if (EthernetManager.instance.shutClientListen == true)break;}}#endregion#region 服务端/// <summary>/// 回发房间信息/// </summary>/// <param name="remote"></param>public void BackRoomInfo(){EndPoint ep = new IPEndPoint(IPAddress.Any, 0);while (true){byte[] data = new byte[1024];int length = udpSocket.ReceiveFrom(data, ref ep);string s = Encoding.UTF8.GetString(data, 0, length);Debug.Log("收到来自" + ep + "的信息" + s);if (s.Equals("RequestServer")){RoomInfo info = new RoomInfo(){name = name,number = EthernetManager.instance.ClientList.Count+1,maxNumber = 8,Ipport = host.ToString()};string jsonStr = JsonMapper.ToJson(info);SendMessage(ep, jsonStr);}}}#endregion/// <summary>/// UDP监听/// </summary>public string Listen(out EndPoint ep){ep = new IPEndPoint(IPAddress.Any, 0);byte[] data = new byte[1024];int length = udpSocket.ReceiveFrom(data, ref ep);string s = Encoding.UTF8.GetString(data, 0, length);return s;//clientIplist.Add(remote);}public void SendMessage(EndPoint ip, string str){byte[] data = Encoding.UTF8.GetBytes(str);udpSocket.SendTo(data, ip);}public void Close(){udpSocket.Close();}
}

监听连接的TCP端

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using LitJson;
public class SocketTcpServer  {Socket tcpServer = null;public EndPoint host=null;public SocketTcpServer(EndPoint host){tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);tcpServer.Bind(host);this.host = host;}public void Listen(){tcpServer.Listen(7);int key=1;while(true){Socket tcp= tcpServer.Accept();Client client=new Client(tcp);EthernetManager.instance.ClientList.Add(key, client);}}public void Close(){tcpServer.Close();}
}

监听到连接后,创建新的socket和客户端连接,单独分装到了Client中,并且在EthernetManager中用Dictionary<id,Client>来管理

Client代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.Threading;
using System.Text;public class Client
{public string username;private Socket clientSocket;private Thread t;private byte[] data = new byte[1024];public Client(Socket clientSocket){this.clientSocket = clientSocket;t = new Thread(ReceiveMessage);t.IsBackground = true;t.Start();}public void ReceiveMessage(){while (true){int length = clientSocket.Receive(data);string str = Encoding.UTF8.GetString(data,0,length);string[] temp = str.Split(':');if (temp[0].Equals(EthernetOrder.RequestMemberInfo.ToString())){username = temp[1];EthernetManager.instance.BroadRoomInfo();}}}public void SendMessage(string str){byte[] byt = Encoding.UTF8.GetBytes(str);clientSocket.Send(byt);}public void ShutDown(){t.Abort();clientSocket.Close();}}

Tcp的客户端代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using LitJson;
using System;
public class SocketTcpClient  {public Socket tcpClient=null;public EndPoint server;private byte[] byt = new byte[1024];public SocketTcpClient(EndPoint Server,EndPoint client){tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);tcpClient.Bind(client);server = Server;}public void Connect(){tcpClient.Connect(server);Debug.Log("TCP连接成功");}public void SendMessage(string str){byte[] data = Encoding.UTF8.GetBytes(str);tcpClient.Send(data);}public void Receive(){while(true){int length=tcpClient.Receive(byt);string str = Encoding.UTF8.GetString(byt,0,length);EthernetManager.instance.clientReceiveMessage = str;EthernetManager.instance.ReceiveAction(JsonMapper.ToObject<List<RoomMemberInfo>>(EthernetManager.instance.clientReceiveMessage));}}public void Close(){tcpClient.Close();}
}

主要代码基本就这些,其余和UI交互的代码就不贴了,如果这样看比较乱等会我打包上传下,可以直接下载我的源码,运行起来查找引用什么的比较方便。

http://download.csdn.net/detail/kzhenguo/9910612

局域网游戏联机大厅建主模式实现附(Unity)相关推荐

  1. 游戏设计模式:命令模式(以Unity开发坦克大战为例)

    命令模式 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式.请求以命令的形式包裹在对象中,并传给调用对象.调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应 ...

  2. rust 局域网联机_腐蚀怎么搭建服务器联机 游戏联机方法一览

    联机玩法是游戏中非常有趣的一种模式,不过许多玩家目前似乎对腐蚀怎么搭建服务器联机不太了解,为了让大家可以更顺利的上手,于是小编这里就为大家带来了游戏联机方法的详细介绍,如果你也对此有疑问的话就一起了解 ...

  3. 多人局域网游戏纯蓝图

    多人局域网游戏学习笔记: 视频网址: https://www.bilibili.com/video/av42635205?from=search&seid=133006399851198673 ...

  4. 局域网steam联机_【联机专题】胡闹厨房2未加密联机版

    十一假日联机专题 <胡闹厨房2>中文未加密联机版 游戏介绍 <胡闹厨房2>来了,带着全新的烹饪行动!重返洋葱王国,通过经典的本地合作模式或是在线游戏模式组建多达四人的大厨团队. ...

  5. mc1.8.1怎么局域网java_我的世界局域网怎么联机 MC局域网联机详细教程

    来源: 网络 MC我的世界是经典的沙盒游戏,游戏自由度非常高,在这款游戏中除了单人模式之外还可以进行联机和小伙伴们一起游戏,那么如何才能够个宿舍中的小伙伴一起玩呢?下面小笨为大家带来局域网联机方法,希 ...

  6. Unity设计模式——享元模式(附代码)

    Unity设计模式--享元模式(附源码) 享元Flyweight模式是什么 享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的 ...

  7. 使用Hook拦截sendto函数解决虚拟局域网部分游戏联机找不到房间的问题——以文明6为例

    正文 重要提醒(2023-02-13):本文部分内容存在bug,目前正在调试修改,会在一段时间之后更新 重要提醒(2023-02-14):目前已修复主要bug,会在一段时间之后更新,本文计划重写大部分 ...

  8. 方舟建服务器局域网显示,《方舟:生存进化》局域网怎么联机 局域网联机教程分享...

    导 读 小编今天给各位小伙伴们介绍一下方舟如何进行局域网联机游戏,一起看看吧. 联机教程" src="http://image.9game.cn/2019/7/30/8929356 ...

  9. 内网计算机游戏不被检测,两台未联网的Win7电脑建立局域网游戏的方法

    说到游戏,可能很多同学都会想到在学校时的光辉岁月,每当看到多个同学在玩同一个游戏时,就会有一个疑问,两台未联网的Win7电脑怎么建立局域网游戏的?下面小编将为大家分享两台未联网的Win7电脑建立局域网 ...

最新文章

  1. 一次mysql瘫痪解救
  2. PHP clone() 函数克隆对象
  3. buu Rabbit
  4. 开启win7 FTP 服务 无法登陆的原因
  5. 黑色幽默:“新知青”电影《走着瞧》首映
  6. pc显示器分辨率 前端_明基透露索尼PS5可提供1440p分辨率选项
  7. 深度 || 既然C编译器是C语言写的,那么第一个C编译器是怎样来的?
  8. Struts2的属性驱动与模型驱动的区别
  9. Python进行UDP编程
  10. java并发编程入门_Java并发编程从入门到精通 PDF 下载
  11. 如何衡量一篇英语作文词汇丰富度?
  12. 获取指定年、月的具体天数
  13. 利用adb卸载手机预装软件(系统软件)
  14. OSPF虚链路(学习笔记+实验验证)
  15. 使用java数据结构编写代码
  16. LRN和BN的数学公式理解与区别
  17. 密歇根安娜堡大学的计算机科学教授,密歇根大学安娜堡分校计算机科学与工程研究生offer及申请要求...
  18. 中国石油大学《计算机文化基础》第一次在线作业
  19. 在Padavan上Open*** Server服务端遇到的几个调测问题
  20. 原创 人脸检测 RetinaFace

热门文章

  1. JAVA Date 工具类 常用
  2. eBay Inc(EBAY)2020年第三季度收益电话会议记录
  3. windows10小技巧: 将手机投影到windows10上
  4. html中怎麼添加箭头,html – 向滚动条添加箭头
  5. 论文笔记+模型实现TransNets: Learning to Transform for Recommendation
  6. python-OpenCv调用IP摄像头APP
  7. 【Chrome 调试技巧】教你一步不用安装插件就可以完成--电脑页面截图
  8. 人工智能和人类智能的本质区别是什么(五)
  9. 只有程序员才能看懂的趣图,第二个我就忍不住了哈哈哈哈!
  10. 南大通用GBase XDM支持的操作平台