/* * MIT License * * Copyright (c) 2018 Clark Yang * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ using Loxodon.Framework.Examples.Messages; using Loxodon.Framework.Net.Connection; using System; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using UnityEngine; namespace Loxodon.Framework.Examples { public class ConnectorExample : MonoBehaviour { Server server; DefaultConnector connector; ISubscription eventSubscription; ISubscription idleEventSubscription; ISubscription messageSubscription; int port = 8000; void Start() { //初始化服务器 server = new Server(port); //开启TLS加密,这是可选的,可用不设置 TextAsset textAsset = Resources.Load("vovgou.pfx"); X509Certificate2 cert = new X509Certificate2(textAsset.bytes, "123456"); server.Secure(true, cert, (sender, certificate, chain, sslPolicyErrors) => { //服务器设置不要求客户端证书,服务器方不校验客户端的协议,直接返回true return true; }); //---------------------- //创建TcpChannel,如果游戏协议没有定义握手消息,那么HandshakeHandler可以为null var channel = new TcpChannel(new DefaultDecoder(), new DefaultEncoder()); channel.NoDelay = true; channel.IsBigEndian = true;//默认使用大端字节序,一般网络字节流用大端 //如果服务器没有开启TLS加密,可用不设置 channel.Secure(true, "vovgou.com", null, (sender, certificate, chain, sslPolicyErrors) => { //客户端方校验服务器端的自签名协议 if (sslPolicyErrors == SslPolicyErrors.None) return true; if (certificate != null && certificate.GetCertHashString() == "3C33D870E7826E9E83B4476D6A6122E497A6D282") return true; return false; }); //HandshakeHandler 不要放在Channel中,请放入Connector中,这样更合理 IHandshakeHandler handshakeHandler = new HandshakeHandler(); //每个20秒空闲则触发空闲事件,并且每隔20秒触发一次,时间为0则关闭空闲事件,首次空闲First为true。示例中只开启了读写都空闲的事件 IdleStateMonitor idleStateMonitor = new IdleStateMonitor(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(20f)); connector = new DefaultConnector(channel, idleStateMonitor, handshakeHandler); connector.AutoReconnect = true;//开启自动重连,只重连一次,失败后不再重试,建议使用心跳包保证连接可用 //订阅事件,收到ConnectionEventArgs参数 eventSubscription = connector.Events().Filter(e => { //消息过滤,只订阅ConnectionEventArgs类型的事件 //if (e is ConnectionEventArgs) // return true; //return false; return true; }).ObserveOn(SynchronizationContext.Current).Subscribe((e) => { Debug.LogFormat("Client Received Event:{0}", e); }); //订阅通知 //使用ObserveOn(SynchronizationContext.Current)切换消息处理线程为当前的UI线程 messageSubscription = connector.Received().Filter(notification => { //过滤消息,只监听CommandID在0-100之间的消息 if (notification.CommandID > 0 && notification.CommandID <= 100) return true; return false; }).ObserveOn(SynchronizationContext.Current).Subscribe(notification => { Debug.LogFormat("Client Received Notification:{0}", notification); }); //订阅连接空闲事件,发生心跳消息 idleEventSubscription = connector.Events() .Filter(e => e is IdleStateEventArgs) .ObserveOn(SynchronizationContext.Current) .Subscribe(e => { try { if (e is IdleStateEventArgs idleStateEventArgs) { if (idleStateEventArgs.IsFirst && (idleStateEventArgs.State == IdleState.ReaderIdle)) { //send a ping message SendHeartbeatMessage(); } } } catch (Exception) { } }); } async void Connect() { try { await connector.Connect("127.0.0.1", port, 1000); Debug.LogFormat("连接成功"); } catch (Exception e) { Debug.LogFormat("连接异常:{0}", e); } } async void Send(Request request) { try { Response response = await connector.Send(request); Debug.LogFormat("The client received a response message successfully,Message:{0}", response); } catch (Exception e) { Debug.LogFormat("The client failed to send a request,Exception:{0}", e); } } async void Send(Notification notification) { try { await connector.Send(notification); Debug.LogFormat("The client has successfully sent a notification"); } catch (Exception e) { Debug.LogFormat("The client failed to send a notification,Exception:{0}", e); } } async void SendHeartbeatMessage() { try { Request request = new Request(); request.CommandID = 0; request.ContentType = 0; request.Content = Encoding.UTF8.GetBytes("ping"); Response response = await connector.Send(request); } catch (TimeoutException e) { //Timeout if (connector.State == ConnectionState.Connected) await connector.Reconnect(); } catch (Exception e) { //Exception Debug.LogFormat("{0}", e); } } void OnGUI() { int x = 50; int y = 50; int width = 200; int height = 100; int i = 0; int padding = 10; GUI.skin.button.fontSize = 25; if (GUI.Button(new Rect(x, y + i++ * (height + padding), width, height), server.Started ? "Stop Server" : "Start Server")) { if (server.Started) server.Stop(); else server.Start(); } if (GUI.Button(new Rect(x, y + i++ * (height + padding), width, height), connector.Connected ? "Disconnect" : "Connect")) { if (connector.Connected) _ = connector.Disconnect(); else Connect(); } if (GUI.Button(new Rect(x, y + i++ * (height + padding), width, height), "Send Request")) { Request request = new Request(); request.CommandID = 20; request.ContentType = 0; request.Content = Encoding.UTF8.GetBytes("this is a request."); Send(request); } if (GUI.Button(new Rect(x, y + i++ * (height + padding), width, height), "Send Notification")) { Notification notification = new Notification(); notification.CommandID = 10; notification.ContentType = 0; notification.Content = Encoding.UTF8.GetBytes("this is a notification."); Send(notification); } } private void OnDestroy() { if (eventSubscription != null) { eventSubscription.Dispose(); eventSubscription = null; } if (idleEventSubscription != null) { idleEventSubscription.Dispose(); idleEventSubscription = null; } if (messageSubscription != null) { messageSubscription.Dispose(); messageSubscription = null; } if (connector != null) { _ = connector.Shutdown(); connector.Dispose(); connector = null; } if (server != null) { server.Stop(); server = null; } } } }