diff --git a/Hander/MultiUserHandle.cs b/Hander/MultiUserHandle.cs index 1f17688..fa7f6b4 100644 --- a/Hander/MultiUserHandle.cs +++ b/Hander/MultiUserHandle.cs @@ -16,7 +16,7 @@ namespace OnlineUserPool.Hander private MultiUserModel model; private TurfHelper _turfHelper; private int _routeId = 1255; //5593; //1660; - private int _size = 50; + private int _size = 150; private int _competitionid = 0;//35; public MultiUserHandle() { @@ -33,8 +33,8 @@ namespace OnlineUserPool.Hander #region sis - //_routeId = 5710; - //_competitionid = 37; + _routeId = 3386; + _competitionid = 1048; #endregion model = await BaseApi.GetAsync($"/Map/sss?routeId={ _routeId }&size={ _size }"); diff --git a/Model/HostModel.cs b/Model/HostModel.cs index 1055329..1e2382d 100644 --- a/Model/HostModel.cs +++ b/Model/HostModel.cs @@ -20,14 +20,31 @@ namespace OnlineUserPool.Model SetProperty(ref _LastActiveTime, value); } } + private int _RouteId = 0; /// /// 地图编号 /// - public int RouteId { get; set; } + public int RouteId { + get { + return _RouteId; + } + set { + SetProperty(ref _RouteId, value); + } + } + + private int _MemberId; /// /// 用户Id /// - public int MemberId { get; set; } + public int MemberId { + get { + return _MemberId; + } + set { + SetProperty(ref _MemberId, value); + } + } /// /// 客户端过期 @@ -51,15 +68,37 @@ namespace OnlineUserPool.Model /// public List ShowRouteId { get; set; } + private int _Competitionid = 0; + public int Competitionid { + get { + return _Competitionid; + } set { + SetProperty(ref _Competitionid, value); + } + } + ///// ///// 是否展示在线的人 ///// //public bool ShowVirtual { get; set; } + public string Encoding { get; set; } + public string Client { get; set; } + + private int _V = 0; /// /// 客户端支持的消息格式的版本号 /// - public int V { get; set; } + public int V { + get + { + return _V; + } + set + { + SetProperty(ref _V, value); + } + } [Newtonsoft.Json.JsonIgnore] public IService Service { get; set; } @@ -72,5 +111,15 @@ namespace OnlineUserPool.Model return Service.ToString(); } } + + private bool _IsWatch; + public bool IsWatch + { + get { return _IsWatch; } + set + { + SetProperty(ref _IsWatch, value); + } + } } } diff --git a/Model/MsgModel.cs b/Model/MsgModel.cs index a72a335..d27bc85 100644 --- a/Model/MsgModel.cs +++ b/Model/MsgModel.cs @@ -5,7 +5,7 @@ using System.Text; namespace OnlineUserPool.Model { - public class MsgModel + public class MsgModel : ReceiveModel { public int RouteId { get; set; } public int MemberId { get; set; } @@ -22,14 +22,6 @@ namespace OnlineUserPool.Model public double PreDistance { get; set; } public double EndDistance { get; set; } - /// - /// 是否展示在线的人 - /// - //public bool ShowVirtual { get; set; } - /// - /// 命令类型,0命令,1消息 - /// - public byte CommandType { get; set; } //public bool IsVirtual { get; set; } @@ -43,10 +35,9 @@ namespace OnlineUserPool.Model public int Competitionid { get; set; } public bool Saved { get; set; } - /// - /// 能处理的消息格式的版本号 - /// - public int V { get; set; } + + //public bool ShowVirtual { get; set; } + public string ToString(int v) diff --git a/Model/ReceiveModel.cs b/Model/ReceiveModel.cs new file mode 100644 index 0000000..a111c0d --- /dev/null +++ b/Model/ReceiveModel.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OnlineUserPool.Model +{ + public class ReceiveModel + { + /// + /// 命令类型,0ping命令,1消息, 2设置客户端信息, 3观察者模式 + /// + public byte CommandType { get; set; } + + /// + /// 能处理的消息格式的版本号 + /// + public int V { get; set; } + } + + public class SetClientCommand : ReceiveModel + { + + public string Encoding { get; set; } + + public string Client { get; set; } + + public int Competitionid { get; set; } + + public bool IsWatch { get; set; } + + public int MemberId { get; set; } + } + + public class SetWatchCommand :ReceiveModel + { + public int Competitionid { get; set; } + } +} diff --git a/Model/TargetData.cs b/Model/TargetData.cs index 1561aaf..d8d99bf 100644 --- a/Model/TargetData.cs +++ b/Model/TargetData.cs @@ -7,7 +7,10 @@ namespace OnlineUserPool.Model public class TargetData { public double _Power = 0d; - public double _Speed = 0; + /// + /// km/h + /// + public double _Speed { get; set; } = 0; public double _Distance = 0D; public double? _Cadence = 0; public int? _HeartRate = 0; diff --git a/Services/IService.cs b/Services/IService.cs index 51bd1d8..beeeb4b 100644 --- a/Services/IService.cs +++ b/Services/IService.cs @@ -8,7 +8,7 @@ namespace OnlineUserPool.Services { public interface IService { - public void RunServer(Action action); + public void RunServer(Action received, Action disconnected = null); public void Send(byte[] dgram, int bytes, IPEndPoint endPoint); diff --git a/Services/TcpService1.cs b/Services/TcpService1.cs index 3ec0e53..70b42a2 100644 --- a/Services/TcpService1.cs +++ b/Services/TcpService1.cs @@ -1,4 +1,5 @@ using NetCoreServer; +using Newtonsoft.Json; using OnlineUserPool.Model; using OnlineUserPool.Unility; using Serilog; @@ -21,35 +22,40 @@ namespace OnlineUserPool.Services //throw new NotImplementedException(); } - public void RunServer(Action action) + public void RunServer(Action action, Action disconnected = null) { //throw new NotImplementedException(); //_action = action; _server = new PfTcpServer(IPAddress.Any, ConfigHelp.TcpPort, (ip, model)=> { action(ip, model, this); - }); + }, disconnected); //_server.OptionNoDelay = true; _server.Start(); } - - public void Send(byte[] dgram, int bytes, IPEndPoint endPoint) { if (dgram == null || !dgram.Any()) { return; } - _server.Multicast(dgram); - + //_server.Multicast(dgram); + var session = _server.FindSession(endPoint); + if(session != null && session.IsConnected) + { + session.Send(dgram); + } } class PfTcpServer : TcpServer { - Action _action; - public PfTcpServer(IPAddress address, int port, Action action) :base(address, port) + private Action _action; + private Action _disconnected; + //private System.Collections.Generic.Dictionary clients = new Dictionary(); + public PfTcpServer(IPAddress address, int port, Action action, Action disconnected = null) :base(address, port) { _action = action; + _disconnected = disconnected; } protected override TcpSession CreateSession() @@ -63,24 +69,46 @@ namespace OnlineUserPool.Services //base.OnError(error); Debug.WriteLine(error); } + + public TcpSession FindSession(IPEndPoint ip) + { + var item = this.Sessions.Values.FirstOrDefault(t => t.Socket.RemoteEndPoint.ToString() == ip.ToString()); + return item; + } + + protected override void OnDisconnected(TcpSession session) + { + //var bbb = this.Sessions.GetValueOrDefault(session.Id); + + var bbb = (session as PfTcpSession); + if(bbb.IPEndPoint != null) + { + _disconnected?.Invoke(bbb.IPEndPoint); + } + base.OnDisconnected(session); + } } class PfTcpSession : TcpSession { - Action _action; - public PfTcpSession(TcpServer server, Action action) : base(server) { + Action _action; + public IPEndPoint IPEndPoint { get; private set; } + public PfTcpSession(TcpServer server, Action action) : base(server) { _action = action; + } protected override void OnConnected() { - //base.OnConnected(); + base.OnConnected(); Debug.WriteLine("有新的Tcp连接"); + + IPEndPoint = IPEndPoint.Parse(this.Socket.RemoteEndPoint.ToString()); } protected override void OnDisconnected() { - //base.OnDisconnected(); + base.OnDisconnected(); Debug.WriteLine("Tcp断开连接"); } private string temp = ""; @@ -102,10 +130,25 @@ namespace OnlineUserPool.Services if(item == '}') { temp += item; - var msg = Newtonsoft.Json.JsonConvert.DeserializeObject(temp); + Debug.WriteLine("收到消息:"+temp); + var msg = JsonConvert.DeserializeObject(temp); var ipEndPoint = IPEndPoint.Parse(this.Socket.RemoteEndPoint.ToString()); - _action(ipEndPoint, msg); - + if (msg.CommandType == 1) + { + _action(ipEndPoint, JsonConvert.DeserializeObject(temp)); + } + else if (msg.CommandType == 2) + { + _action(ipEndPoint, JsonConvert.DeserializeObject(temp)); + } + //else if(msg.CommandType == 3) + //{ + // _action(ipEndPoint, JsonConvert.DeserializeObject(temp)); + //} + else + { + _action(ipEndPoint, msg); + } temp = ""; } else diff --git a/Services/UdpService.cs b/Services/UdpService.cs index 55eb033..413a802 100644 --- a/Services/UdpService.cs +++ b/Services/UdpService.cs @@ -1,4 +1,5 @@ -using OnlineUserPool.Model; +using Newtonsoft.Json; +using OnlineUserPool.Model; using OnlineUserPool.Unility; using System; using System.Collections.Generic; @@ -21,7 +22,7 @@ namespace OnlineUserPool.Services //serverIpEndPoint = new IPEndPoint(IPAddress.Parse(ConfigHelp.Ip), ConfigHelp.Port); } - public void RunServer(Action action) + public void RunServer(Action action, Action disconnected = null) { udpServer = new UdpClient(ConfigHelp.UdpPort); uint IOC_IN = 0x80000000; @@ -37,14 +38,25 @@ namespace OnlineUserPool.Services { byte[] receiveBytes = udpServer.Receive(ref remoteIpEndPoint); var returnData = Encoding.ASCII.GetString(receiveBytes); - var msg = Newtonsoft.Json.JsonConvert.DeserializeObject(returnData); + var msg = JsonConvert.DeserializeObject(returnData); //lock (locker) { #if DEBUG WriteLine($"本次接收:{ remoteIpEndPoint.Address.ToString() }:{ remoteIpEndPoint.Port }收到消息:{ returnData }"); #endif - action(remoteIpEndPoint, msg, this); + if (msg.CommandType == 1) + { + action(remoteIpEndPoint, JsonConvert.DeserializeObject(returnData), this); + } + else if (msg.CommandType == 2) + { + action(remoteIpEndPoint, JsonConvert.DeserializeObject(returnData), this); + } + else + { + action(remoteIpEndPoint, msg, this); + } //if (timer.AutoReset == false) //{ diff --git a/Unility/CommonHelper.cs b/Unility/CommonHelper.cs index 990e736..82e8f2a 100644 --- a/Unility/CommonHelper.cs +++ b/Unility/CommonHelper.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.IO; +using System.IO.Compression; using System.Security.Cryptography; using System.Text; using TurfCS; @@ -23,5 +25,42 @@ namespace OnlineUserPool.Unility new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer); return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max); } + + public static byte[] Compress(string text) + { + var bytes = Encoding.UTF8.GetBytes(text); + return Compress(bytes); + } + + public static byte[] Compress(byte[] bytes) + { + //var bytes = Encoding.Unicode.GetBytes(text); + using (var mso = new MemoryStream()) + { + using (var gs = new GZipStream(mso, CompressionMode.Compress)) + { + gs.Write(bytes, 0, bytes.Length); + } + return mso.ToArray(); + } + } + + public static string Decompress(byte[] data) + { + // Read the last 4 bytes to get the length + byte[] lengthBuffer = new byte[4]; + Array.Copy(data, data.Length - 4, lengthBuffer, 0, 4); + int uncompressedSize = BitConverter.ToInt32(lengthBuffer, 0); + + var buffer = new byte[uncompressedSize]; + using (var ms = new MemoryStream(data)) + { + using (var gzip = new GZipStream(ms, CompressionMode.Decompress)) + { + gzip.Read(buffer, 0, uncompressedSize); + } + } + return Encoding.UTF8.GetString(buffer); + } } } diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs index 3ba82fb..587b048 100644 --- a/ViewModels/MainWindowViewModel.cs +++ b/ViewModels/MainWindowViewModel.cs @@ -16,6 +16,8 @@ using System.Diagnostics; using OnlineUserPool.Services; using Newtonsoft.Json; using System.Collections.Concurrent; +using System.IO.Compression; +using System.IO; namespace OnlineUserPool.ViewModels { @@ -61,11 +63,11 @@ namespace OnlineUserPool.ViewModels //Customers.Add("suntao"); WriteLine(DateTime.Now.ToShortDateString()); LogHelper.Init(); - mapRecordRankingHander = new MultiUserHandle();//new MapRecordRankingHander(); + mapRecordRankingHander = new MapRecordRankingHander();//new MultiUserHandle();// //mapRecordRankingHander = new MultiUserHandle(); Log.Information($"初始化连接,当前地址:{ IPAddress.Any }:{ConfigHelp.UdpPort}"); - new TcpService1().RunServer(ReceivedData); + new TcpService1().RunServer(ReceivedData, CientDisconnected); //var tet = new System.Collections.Concurrent.ConcurrentBag(); var _udpService = new UdpService(); @@ -81,7 +83,7 @@ namespace OnlineUserPool.ViewModels //Console.ReadKey(); } - private void ReceivedData(IPEndPoint remoteIpEndPoint, MsgModel msg, IService service) + private void ReceivedData(IPEndPoint remoteIpEndPoint, ReceiveModel msg, IService service) { dispatcher.Invoke(() => { @@ -91,24 +93,76 @@ namespace OnlineUserPool.ViewModels { IPEndPoint = remoteIpEndPoint, LastActiveTime = DateTime.Now, - RouteId = msg.RouteId, - MemberId = msg.MemberId, - //ShowVirtual = msg.ShowVirtual - V = msg.V, + //RouteId = msg.RouteId, + //MemberId = msg.MemberId, + //V = msg.V, + //Encoding = msg.Encoding, + //Client = msg.Client, Service = service }); } + switch (msg.CommandType) + { + //ping命令 + case 0: + { + var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint)); + client.LastActiveTime = DateTime.Now; + if(msg.V > 0) + { + client.V = msg.V; + } + } + break; + //消息 + case 1: + { + var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint)); + var msg1 = (msg as MsgModel); + client.Competitionid = msg1.Competitionid; + if (msg1.V > 0) + { + client.V = msg1.V; + } + receiveMes.Add(msg1); + } + break; + //设置客户端 + case 2: + { + var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint)); + var msg1 = (msg as SetClientCommand); + client.Encoding = msg1.Encoding; + client.Client = msg1.Client; + client.V = msg1.V; + client.MemberId = msg1.MemberId; + client.Competitionid = msg1.Competitionid; + client.IsWatch = msg1.IsWatch; + } + break; + //case 3: + // { + // var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint)); + // var msg1 = (msg as SetWatchCommand); + // client.Competitionid = msg1.Competitionid; + // client.IsWatch = true; + // } + // break; + default: + break; + } if (msg.CommandType == 0) { - var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint)); - client.LastActiveTime = DateTime.Now; + + //client.ShowVirtual = msg.ShowVirtual; } else if (msg.CommandType == 1) { - receiveMes.Add(msg); + } + }); } @@ -125,7 +179,7 @@ namespace OnlineUserPool.ViewModels { dispatcher.Invoke(() => { - Customers.Clear(); + //Customers.Clear(); //删除已经发送的数据 //receiveMes.Clear(); @@ -142,7 +196,7 @@ namespace OnlineUserPool.ViewModels } lock (locker) { - var list = CloneJson(receiveMes).ToList(); + var msgs = CloneJson(receiveMes).ToList(); receiveMes.Clear(); //加入虚拟人物消息 @@ -150,17 +204,17 @@ namespace OnlineUserPool.ViewModels WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "-当前在线人数:" + Clients.Count + "-当前虚拟人数:" + virtualData.Count); #if DEBUG - WriteLine($"在线人:{Newtonsoft.Json.JsonConvert.SerializeObject(list)}"); + WriteLine($"在线人:{Newtonsoft.Json.JsonConvert.SerializeObject(msgs)}"); //\r\n虚拟人:{Newtonsoft.Json.JsonConvert.SerializeObject(virtualData)} #endif - list.AddRange(virtualData); + msgs.AddRange(virtualData); - SendMessage(Clients, list); + SendMessage(Clients, msgs); dispatcher.Invoke(() => { Customers.Clear(); - foreach (var item in list) + foreach (var item in msgs) { if (Customers.Any(c => c.MemberId == item.MemberId)) { @@ -170,12 +224,12 @@ namespace OnlineUserPool.ViewModels } //移除下线的客户端 - for (int i = 0; i < list.Count; i++) + for (int i = 0; i < msgs.Count; i++) { - if (list[i].exit && list[i].MemberId >0)//客户端退出,并且不是虚拟的人物 + if (msgs[i].exit && msgs[i].MemberId >0)//客户端退出,并且不是虚拟的人物 { //这个地方有严重的逻辑错误(虚拟的人物不能和真实的人用同一个名字) - var info = Clients.FirstOrDefault(n => n.MemberId == list[i].MemberId); + var info = Clients.FirstOrDefault(n => n.MemberId == msgs[i].MemberId); if (info != null) { Clients.Remove(info); @@ -218,7 +272,7 @@ namespace OnlineUserPool.ViewModels item.EndDistance = Math.Round(item.EndDistance, 5); item.WeightKg = Math.Round(item.WeightKg, 2); } - string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(list.Select(m=> new { + string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(list.Select(m=> new { m.RouteId, m.MemberId, m.Point, @@ -240,13 +294,29 @@ namespace OnlineUserPool.ViewModels { strV1 += "|"; } + + + //strV1 = "abcdefghijklmnopqrstuvwxyz"; //System.IO.File.AppendAllText(System.Environment.CurrentDirectory + "要发送的数据.txt", strV1.Trim().Replace("\0", "") + "\r\n", Encoding.UTF8); WriteLine(strV1); WriteLine(strV1.Length.ToString()); - var dataV1 = Encoding.ASCII.GetBytes(strV1); - var dataV1ForUdp = strV1.Last() == '|' ? Encoding.ASCII.GetBytes(strV1.Substring(0, strV1.Length - 1)) : dataV1; - SendDataSize = $"\t单客户端数据包V1:{ (strV1.Length/1000D) }KB,一共占用带宽:{ strV1.Length / 1000D * clients1.Count }KB"; + //var dataV1 = Encoding.UTF8.GetBytes(strV1); + //var dataV1ForUdp = strV1.Last() == '|' ? Encoding.ASCII.GetBytes(strV1.Substring(0, strV1.Length - 1)) : dataV1; + //var dataCompressV1 = Encoding.UTF8.GetBytes($"*{ Convert.ToBase64String(CommonHelper.Compress(strV1)) }#"); + + + var data1 = new Data1(strV1); + var data1ForUdp = new Data1(strV1.Last() == '|' ? strV1.Substring(0, strV1.Length - 1) : strV1); + + //var watchList = string.Join('|', clients1.Where(c => c.IsWatch).Select(c => c.MemberId)); + //var strV2 = $"*l{{{ strV1 }}};w{{{ watchList }}}#"; + ////var dataCompressV2 = Encoding.UTF8.GetBytes($"{ Convert.ToBase64String(CommonHelper.Compress(strV2)) }"); + //var data2 = new Data1(strV2); + + SendDataSize = $"\t单客户端数据包V1:{ data1.GetBytes().Length/1000D }KB,一共占用带宽:{ data1.GetBytes().Length / 1000D * clients1.Count }KB,压缩以后{ data1.GetBytes(true).Length/1000D * clients1.Count }KB"; + + #region udp发送数据 foreach (var item in clients1) { try @@ -255,19 +325,44 @@ namespace OnlineUserPool.ViewModels { continue; } - if((item.Service is UdpService) ==false) + //if((item.Service is UdpService) ==false) + //{ + // continue; + //} + if (item.V != 1 && item.V != 2) { + item.Service.Send(data, data.Length, item.IPEndPoint); continue; } - if (item.V == 1) + if (item.V == 1 && item.Service is UdpService) { - - item.Service.Send(dataV1ForUdp, dataV1ForUdp.Length, item.IPEndPoint); + item.Service.Send(data1ForUdp.GetBytes(), data1ForUdp.GetBytes().Length, item.IPEndPoint); } else { - item.Service.Send(data, data.Length, item.IPEndPoint); - } + Data1 ddd = null;// = item.V == 1 ? ; + if (item.V == 1) + { + ddd = data1; + } + else if (item.V == 2) + { + //ddd = data2; + + var temp = string.Join("|", list.Where(m => m.Competitionid == item.Competitionid).Select(m => m.ToString(1))) + "|"; + + var watchList1 = string.Join('|', clients1.Where(c => c.IsWatch && c.Competitionid == item.Competitionid).Select(c => c.MemberId)); + var strV21 = $"*l{{{ temp }}};w{{{ watchList1 }}}#"; + ddd = new Data1(strV21); + + } + else + { + continue; + } + bool isZip = item.Encoding == "gzip"; + item.Service.Send(ddd.GetBytes(isZip), ddd.GetBytes(isZip).Length, item.IPEndPoint); + } } catch (Exception e) { @@ -275,11 +370,15 @@ namespace OnlineUserPool.ViewModels Log.Error($"{ item.IPEndPoint.ToString() }:{ e.Message }\r\n{ e.StackTrace }"); } } - var tcpClient = clients1.Where(c => c.Service is TcpService1).FirstOrDefault(); - if(tcpClient != null) - { - tcpClient.Service.Send(dataV1, dataV1.Length, null); - } + #endregion + + //#region tcp发送数据 + //var tcpClient = clients1.Where(c => c.Service is TcpService1).FirstOrDefault(); + //if(tcpClient != null) + //{ + // tcpClient.Service.Send(dataV1, dataV1.Length, null); + //} + //#endregion } @@ -304,5 +403,50 @@ namespace OnlineUserPool.ViewModels return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), deserializeSettings); } + + private void CientDisconnected(EndPoint point) + { + var client = Clients.FirstOrDefault(f => f.IPEndPoint.ToString() == point.ToString()); + if (client != null) + { + dispatcher.Invoke(() => + { + Clients.Remove(client); + }); + } + } + } + + public class Data1 + { + private string _txt = ""; + public Data1(string txt) + { + this._txt = txt; + } + + public byte[] GetBytes(bool compress = false) + { + if (compress) + { + return GetCompressBytes(); + } + return Encoding.UTF8.GetBytes(this._txt); + } + + + private byte[] GetCompressBytes() + { + var temp = this._txt; + if(temp[0] == '*') + { + temp = temp.Substring(1); + } + if(temp.Last() == '#') + { + temp = temp.Substring(0, temp.Length - 1); + } + return Encoding.UTF8.GetBytes($"*{ Convert.ToBase64String(CommonHelper.Compress(temp)) }#"); + } } }