IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 开源Unity服务端客户端(双端C#)网络通讯框架(Lidgren)[二]ChatDemo之NetServer -> 正文阅读

[游戏开发]开源Unity服务端客户端(双端C#)网络通讯框架(Lidgren)[二]ChatDemo之NetServer

开源Unity服务端客户端(双端C#)网络通讯框架(Lidgren)[二]ChatDemo

还有一点时间,把二也开个头

有服务端和客户端两部分,那先来看服务端,至于到底哪个先哪个后,其实我觉得还是先看服务端。

1.从服务端说起。

创建一个NetServer

创建一个socket需要一些最基础的配置,比如端口号、连接的客户端数等等。NetPeerConfiguration类就是用于配置参数。其中的字符串参数可理解为给它取个名字。就算是ip、port都正确,客户端叫不对名字服务器也不会相应。这样我们可以理解为游戏中常见的:

  • 聊天服务器
  • 战斗服务器
  • 各个副本服务器
NetServer s_server;//声明
NetPeerConfiguration config = new NetPeerConfiguration("chat");//创建一个网络配置类
config.MaximumConnections = 100;//最多有100个连接
			config.Port = 14242;//使用本地14242端口
			s_server = new NetServer(config);//使用配置文件类创建一个socket

2. 开启Server

public static void StartServer()
		{
			s_server.Start();
		}

3 监听消息(msg)

3.1 添加消息处理监听

Application.Idle += new EventHandler(Application_Idle);

3.2 Application_Idle 详解

3.2.1NativeMethods.AppStillIdle

while (NativeMethods.AppStillIdle)
{
	//code1
}

//---------------------------------------------
[StructLayout(LayoutKind.Sequential)]
		public struct PeekMsg
		{
			public IntPtr hWnd;
			public Message msg;
			public IntPtr wParam;
			public IntPtr lParam;
			public uint time;
			public System.Drawing.Point p;
		}
[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
		[DllImport("User32.dll", CharSet = CharSet.Auto)]
		public static extern bool PeekMessage(out PeekMsg msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

		public static bool AppStillIdle
		{
			get
			{
				PeekMsg msg;
				return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
			}
		}

PeekMessage(百科):Windows窗体程序或者说整个windows桌面应用程序,都是通过sendmessage这种方式搭建的。
PeekMessage:

  • 返回值表示:消息队列中是否存在消息;
  • 参数1(PeekMsg):获得一条消息(堆栈指针不移动。)
  • 其他参数:巴拉巴拉(看百科)…

3.1.2回归正题 消息解析

				//code1
				NetIncomingMessage im;//声明一条消息变量
				while ((im = s_server.ReadMessage()) != null)//读消息
				{
					// handle incoming message
					switch (im.MessageType)
					{
							//code2
					}
					s_server.Recycle(im);
				}
				Thread.Sleep(1);
		/// <summary>
		/// Read a pending message from any connection, if any
		/// 从等待队列中尝试读取一条消息(非阻塞)
		/// </summary>
		public NetIncomingMessage ReadMessage()
		{
			NetIncomingMessage retval;
			if (m_releasedIncomingMessages.TryDequeue(out retval))
			{
				if (retval.MessageType == NetIncomingMessageType.StatusChanged)
				{
					NetConnectionStatus status = (NetConnectionStatus)retval.PeekByte();
					retval.SenderConnection.m_visibleStatus = status;
				}
			}
			return retval;
		}

NetPeer.cs

从上面代码可以看出来ReadMessage方法是一个非阻塞的方法Thread.Sleep(1);是必要的。

读取消息的应该非为如下几步:

  • NetIncomingMessage im;//声明一个消息变量
  • while ((im = s_server.ReadMessage()) != null) //使用while循环读取当前队列中所有的消息
  • 在while循环中处理读到消息
  • s_server.Recycle(im);// 回收消息(特别棒的操作减少GC)
  • 如此往复以上内容

3.1.3处理消息

//code2
// handle incoming message
					switch (im.MessageType)
					{
						case NetIncomingMessageType.DebugMessage:
						case NetIncomingMessageType.ErrorMessage:
						case NetIncomingMessageType.WarningMessage:
						case NetIncomingMessageType.VerboseDebugMessage:
							string text = im.ReadString();
							Output(text);
							break;

						case NetIncomingMessageType.StatusChanged:
							NetConnectionStatus status = (NetConnectionStatus)im.ReadByte();

							string reason = im.ReadString();
							Output(NetUtility.ToHexString(im.SenderConnection.RemoteUniqueIdentifier) + " " + status + ": " + reason);

							if (status == NetConnectionStatus.Connected)
								Output("Remote hail: " + im.SenderConnection.RemoteHailMessage.ReadString());

							UpdateConnectionsList();
							break;
						case NetIncomingMessageType.Data:
							// incoming chat message from a client
							string chat = im.ReadString();

							Output("Broadcasting '" + chat + "'");

							// broadcast this to all connections, except sender
							List<NetConnection> all = s_server.Connections; // get copy
							all.Remove(im.SenderConnection);

							if (all.Count > 0)
							{
								NetOutgoingMessage om = s_server.CreateMessage();
								om.Write(NetUtility.ToHexString(im.SenderConnection.RemoteUniqueIdentifier) + " said: " + chat);
								s_server.SendMessage(om, all, NetDeliveryMethod.ReliableOrdered, 0);
							}
							break;
						default:
							Output("Unhandled type: " + im.MessageType + " " + im.LengthBytes + " bytes " + im.DeliveryMethod + "|" + im.SequenceChannel);
							break;
					}

借助一下API文档
NetIncomingMessageType

这里要着重理解的内容是:和平时直接使用socket不同,所有的消息都被封装。
例如:
StatusChanged就如它字面意思那样,有客户端连接变化时会受到消息。
Data:这里是程序的主要逻辑实现,需要自定义各种协议完成项目功能。

4.处理消息

4.1处理连接状态消息

							case NetIncomingMessageType.StatusChanged:
							NetConnectionStatus status = (NetConnectionStatus)im.ReadByte();

							string reason = im.ReadString();
							Output(NetUtility.ToHexString(im.SenderConnection.RemoteUniqueIdentifier) + " " + status + ": " + reason);

							if (status == NetConnectionStatus.Connected)
								Output("Remote hail: " + im.SenderConnection.RemoteHailMessage.ReadString());

							UpdateConnectionsList();
							break;
  • NetConnectionStatus 是表示连接状态的枚举,读取一个字节im.ReadByte()得到状态。
  • 获取状态发生改变的原因。string reason = im.ReadString();
  • 如果是新客户端加入if (status == NetConnectionStatus.Connected)
  • RemoteHailMessage表示远端打招呼消息 im.SenderConnection.RemoteHailMessage.ReadString()
  • 更新连接用户列表UpdateConnectionsList();

4.2 处理聊天消息

							case NetIncomingMessageType.Data:
							// incoming chat message from a client
							string chat = im.ReadString();

							Output("Broadcasting '" + chat + "'");

							// broadcast this to all connections, except sender
							List<NetConnection> all = s_server.Connections; // get copy
							all.Remove(im.SenderConnection);

							if (all.Count > 0)
							{
								NetOutgoingMessage om = s_server.CreateMessage();
								om.Write(NetUtility.ToHexString(im.SenderConnection.RemoteUniqueIdentifier) + " said: " + chat);
								s_server.SendMessage(om, all, NetDeliveryMethod.ReliableOrdered, 0);
							}
							break;
  • 收到A客户端发来的内容。string chat = im.ReadString();
  • 获取除A以外的客户端List all = s_server.Connections;all.Remove(im.SenderConnection);
  • 服务端创建一条外发消息 NetOutgoingMessage om = s_server.CreateMessage();
  • 外发消息中写入(A客户端唯一标识_said:_消息内容)om.Write(NetUtility.ToHexString(im.SenderConnection.RemoteUniqueIdentifier) + " said: " + chat);
  • 以顺序可靠的形式发送消息s_server.SendMessage(om, all, NetDeliveryMethod.ReliableOrdered, 0);

题外话(可略)

话题1.代码应该好看易读

看到这消息处理部分一层一层的嵌套,可能例子中看不出来
举例子还是那个StatusChanged(如下图),
示例只处理connected,实际应用中最少还需要处理disconnected。
Data中有自定义的各种消息逻辑那就更多
写在一个Switch中一定很长。

StatusChanged
话题2.为什么不用现成的PhotonServer+PUN2?

PhotonServer+PUN2和unity结合完美,使用简单,是一个非常好的客户端服务端框架。
没有记错的话,PhotonServer底层Socket使用C/C++编写,上层架构使用C#。
对于刚接触网络的同学们来说,PhotonServer完美的封装,只让开发者看到API并不知所以然。
更有可能没有基础的同学们,API中所说内容都不知道是什么,怎么使用。
总结就是:如果需要快速在项目中使用网络功能首选PhotonServer+PUN2。有时间研究的同学,可以使用Lidgren
一步一步完成属于自己的网络框架。

下一篇:ChatDemo之NetClient粗略带过

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-09-12 13:27:58  更:2021-09-12 13:28:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 13:21:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码